savagedb_bot/main.py
Vlad e589ddafc5 Добавлены новые функции для работы с записями о повреждениях в классе OracleDatabase и обновлены обработчики в main.py:
- Реализован метод count_salvage_records для подсчета записей по VIN.
- Добавлен метод fetch_salvage_detailed_info для получения детальной информации о повреждениях.
- Обновлены обработчики для проверки VIN и получения детальной информации о записях, включая интеграцию с платежной системой.
Эти изменения улучшают функциональность бота и позволяют пользователям получать более полную информацию о состоянии автомобилей.
2025-06-01 12:14:59 +03:00

658 lines
32 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import asyncio
from os import getenv
import logging
from datetime import datetime
from aiogram import Bot, Dispatcher
from aiogram.filters import Command
from aiogram.types import Message, InlineKeyboardButton, InlineKeyboardMarkup, CallbackQuery, LabeledPrice, PreCheckoutQuery
from aiogram.utils.keyboard import InlineKeyboardBuilder
from aiogram.fsm.state import State, StatesGroup
from aiogram.fsm.context import FSMContext
from db import OracleDatabase
from middlewares.db import DbSessionMiddleware
if getenv("DEBUG",'0') == '1':
logging.basicConfig(level=logging.INFO)
else:
logging.basicConfig(level=logging.WARNING)
TOKEN = getenv("BOT_TOKEN")
BOTNAME = getenv("BOT_NAME")
oracle_db = OracleDatabase(
user= getenv("db_user"),
password= getenv("db_password"),
dsn= getenv("db_dsn")
)
dp = Dispatcher()
class VinStates(StatesGroup):
waiting_for_vin = State()
waiting_for_check_vin = State()
# Command handler
@dp.message(Command("start"))
async def command_start_handler(message: Message, db: OracleDatabase = None) -> None:
# Используем переданный db или глобальный oracle_db
database = db or oracle_db
# Сохраняем данные пользователя при каждом взаимодействии
await database.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"
"• Salvage or junk status\n"
"• Damage from hail, flood, or fire\n"
"• Mileage discrepancies or odometer rollback\n"
"• Gray market vehicle status\n\n"
"We don't claim that a vehicle has a salvage title, but we provide information indicating possible past damages, helping you make informed decisions."
)
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.adjust(3)
builder.button(text="Help", callback_data="help")
builder.button(text="Prices", callback_data="prices")
builder.button(text="Go Salvagedb.com", url="https://salvagedb.com")
builder.adjust(3, 2)
await message.answer(welcome_text, reply_markup=builder.as_markup())
@dp.callback_query(lambda c: c.data == "decode_vin")
async def decode_vin_callback(callback: CallbackQuery, state: FSMContext, db: OracleDatabase = None):
# Используем переданный db или глобальный oracle_db
database = db or oracle_db
# Сохраняем данные пользователя при нажатии кнопки
await database.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 == "check_vin")
async def check_vin_callback(callback: CallbackQuery, state: FSMContext, db: OracleDatabase = None):
# Используем переданный db или глобальный oracle_db
database = db or oracle_db
# Сохраняем данные пользователя при нажатии кнопки
await database.save_user(callback.from_user, "check_vin_button")
await callback.message.answer("Please enter the vehicle VIN to check salvage records.")
await state.set_state(VinStates.waiting_for_check_vin)
await callback.answer()
@dp.callback_query(lambda c: c.data == "main_menu")
async def main_menu_callback(callback: CallbackQuery, state: FSMContext, db: OracleDatabase = None):
# Используем переданный db или глобальный oracle_db
database = db or oracle_db
# Сохраняем данные пользователя при возврате в главное меню
await database.save_user(callback.from_user, "main_menu_button")
await state.clear()
await command_start_handler(callback.message, database)
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, db: OracleDatabase = None):
# Используем переданный db или глобальный oracle_db
database = db or oracle_db
# Сохраняем данные пользователя при инициации платежа
await database.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",
description="Get comprehensive vehicle detailed information for 1 Telegram Star",
payload=f"detailed_vin_info:{vin}", # Include VIN in payload
provider_token="", # Empty for Telegram Stars
currency="XTR", # Telegram Stars currency
prices=prices
)
await callback.answer()
@dp.message(VinStates.waiting_for_vin)
async def process_vin(message: Message, state: FSMContext, db: OracleDatabase = None):
# Используем переданный db или глобальный oracle_db
database = db or oracle_db
# Сохраняем данные пользователя при обработке VIN
await database.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:
make, model, year, cnt = await database.fetch_vin_info(vin)
logging.info(f"Decode VIN 1st step: make: {make}, model: {model}, year: {year}, cnt: {cnt}")
response_text = f"🚗 **{year} {make} {model}**\n\n"
if cnt == 0:
response_text = "** Unable to decode VIN, possibly incorrect **"
# Create keyboard based on cnt value
builder = InlineKeyboardBuilder()
builder.button(text="Try another VIN", callback_data="decode_vin")
builder.button(text="Back to Main Menu", callback_data="main_menu")
if cnt > 9:
builder.button(text="Get detailed info. Pay 1 ⭐️", callback_data=f"pay_detailed_info:{vin}", pay=True)
builder.adjust(1, 1, 1) # Each button on separate row
else:
builder.adjust(1, 1) # Each button on separate row
await message.answer(response_text, reply_markup=builder.as_markup(), parse_mode="Markdown")
except Exception as e:
logging.error(f"Database error for VIN {vin}: {e}")
await message.answer("Error retrieving data from database. Please try again later.")
await state.clear()
else:
await message.answer("Invalid VIN. Please enter a valid 17-character VIN (letters and numbers, no I, O, Q).")
@dp.message(VinStates.waiting_for_check_vin)
async def process_check_vin(message: Message, state: FSMContext, db: OracleDatabase = None):
# Используем переданный db или глобальный oracle_db
database = db or oracle_db
# Сохраняем данные пользователя при обработке VIN
await database.save_user(message.from_user, "check_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:
# Получаем базовую информацию о VIN
make, model, year, cnt = await database.fetch_vin_info(vin)
# Получаем количество записей в salvagedb.salvagedb
salvage_count = await database.count_salvage_records(vin)
logging.info(f"Check VIN: make: {make}, model: {model}, year: {year}, cnt: {cnt}, salvage_count: {salvage_count}")
# Формируем текст ответа
if cnt == 0:
response_text = "❌ **Unable to decode VIN, possibly incorrect**\n\n"
else:
response_text = f"🚗 **{year} {make} {model}**\n\n"
response_text += f"📊 **Records found in database:** {salvage_count}\n\n"
# Создаем клавиатуру в зависимости от наличия записей
builder = InlineKeyboardBuilder()
if salvage_count > 0:
# Есть записи - показываем кнопки: Get detailed info, Pay 10⭐, Try another VIN, Back to main menu
builder.button(text="Get detailed info", callback_data=f"get_detailed_info:{vin}")
builder.button(text="Pay 10 ⭐️", callback_data=f"pay_check_detailed:{vin}", pay=True)
builder.button(text="Try another VIN", callback_data="check_vin")
builder.button(text="Back to Main Menu", callback_data="main_menu")
builder.adjust(1, 1, 1, 1) # Each button on separate row
else:
# Нет записей - показываем кнопки: Try another VIN, Back to main menu
response_text += " **No salvage records found for this VIN**"
builder.button(text="Try another VIN", callback_data="check_vin")
builder.button(text="Back to Main Menu", callback_data="main_menu")
builder.adjust(1, 1) # Each button on separate row
await message.answer(response_text, reply_markup=builder.as_markup(), parse_mode="Markdown")
except Exception as e:
logging.error(f"Database error for check VIN {vin}: {e}")
await message.answer("Error retrieving data from database. Please try again later.")
await state.clear()
else:
await message.answer("Invalid VIN. Please enter a valid 17-character VIN (letters and numbers, no I, O, Q).")
@dp.callback_query(lambda c: c.data and c.data.startswith("get_detailed_info:"))
async def get_detailed_info_callback(callback: CallbackQuery, db: OracleDatabase = None):
# Используем переданный db или глобальный oracle_db
database = db or oracle_db
# Сохраняем данные пользователя
await database.save_user(callback.from_user, "get_detailed_info_button")
# Извлекаем VIN из callback data
vin = callback.data.split(":")[1]
try:
# Получаем детальную информацию из таблицы salvagedb.salvagedb
salvage_count = await database.count_salvage_records(vin)
if salvage_count > 0:
response_text = f"🔍 **Detailed Salvage Information for VIN:** {vin}\n\n"
response_text += f"📊 **Total salvage records found:** {salvage_count}\n\n"
response_text += "💡 **This vehicle has salvage records in our database.**\n"
response_text += "For complete detailed analysis including damage history, "
response_text += "accident reports, and comprehensive vehicle information, "
response_text += "please use our paid detailed report service.\n\n"
response_text += "💰 **Price:** 10 ⭐️ (Telegram Stars)"
else:
response_text = f"✅ **Good news!** No salvage records found for VIN: {vin}\n\n"
response_text += "This vehicle appears to have no salvage history in our database."
# Создаем клавиатуру
builder = InlineKeyboardBuilder()
if salvage_count > 0:
builder.button(text="Pay 10 ⭐️ for Full Report", callback_data=f"pay_check_detailed:{vin}", pay=True)
builder.button(text="Try another VIN", callback_data="check_vin")
builder.button(text="Back to Main Menu", callback_data="main_menu")
builder.adjust(1, 1, 1) if salvage_count > 0 else builder.adjust(1, 1)
await callback.message.answer(response_text, reply_markup=builder.as_markup(), parse_mode="Markdown")
except Exception as e:
logging.error(f"Error getting detailed info for VIN {vin}: {e}")
await callback.message.answer("Error retrieving detailed information. Please try again later.")
await callback.answer()
@dp.callback_query(lambda c: c.data and c.data.startswith("pay_check_detailed:"))
async def pay_check_detailed_callback(callback: CallbackQuery, db: OracleDatabase = None):
# Используем переданный db или глобальный oracle_db
database = db or oracle_db
# Сохраняем данные пользователя при инициации платежа
await database.save_user(callback.from_user, "check_payment_initiation")
# Извлекаем VIN из callback data
vin = callback.data.split(":")[1]
prices = [LabeledPrice(label="Detailed Salvage Report", amount=10)]
logging.info(f"Sending invoice for salvage check VIN: {vin}")
await callback.bot.send_invoice(
chat_id=callback.message.chat.id,
title="Detailed Salvage Report",
description="Get comprehensive salvage history and damage information for 10 Telegram Stars",
payload=f"detailed_salvage_check:{vin}", # Уникальный payload для этого типа платежа
provider_token="", # Empty for Telegram Stars
currency="XTR", # Telegram Stars currency
prices=prices
)
await callback.answer()
@dp.pre_checkout_query()
async def pre_checkout_handler(pre_checkout_query: PreCheckoutQuery, db: OracleDatabase = None):
# Используем переданный db или глобальный oracle_db
database = db or oracle_db
# Сохраняем данные пользователя при pre-checkout
await database.save_user(pre_checkout_query.from_user, "pre_checkout")
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 = None):
# Используем переданный db или глобальный oracle_db
database = db or oracle_db
# Проверяем, является ли пользователь администратором
if message.from_user.id != ADMIN_USER_ID:
await message.answer("❌ Access denied. This command is for administrators only.")
return
try:
# Получаем общую статистику
stats = await database.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 = None):
# Используем переданный db или глобальный oracle_db
database = db or oracle_db
# Сохраняем данные о платеже пользователя
await database.save_user(message.from_user, "successful_payment")
payload = message.successful_payment.invoice_payload
# Определяем сумму платежа в зависимости от типа
payment_amount = 10.0 if payload.startswith("detailed_salvage_check:") else 1.0
await database.update_user_payment(message.from_user.id, payment_amount)
if payload.startswith("detailed_vin_info:"):
vin = payload.split(":")[1]
try:
# Get detailed information from database
detailed_info = await database.fetch_detailed_vin_info(vin)
if detailed_info and detailed_info['all_params']:
params = detailed_info['all_params']
# Format the detailed report with all categories
report = f"🚗 **{params.get('model_year', 'N/A')} {params.get('make', 'N/A')} {params.get('model', 'N/A')}**\n"
if params.get('trim'):
report += f"{params.get('trim')}\n"
report += "\n"
# BASIC CHARACTERISTICS
if detailed_info['basic_characteristics']:
report += "📋 **BASIC CHARACTERISTICS**\n"
for key, data in detailed_info['basic_characteristics'].items():
report += f"• **{data['param_name']}:** {data['value']}\n"
report += "\n"
# ENGINE AND POWERTRAIN
if detailed_info['engine_and_powertrain']:
report += "🔧 **ENGINE AND POWERTRAIN**\n"
for key, data in detailed_info['engine_and_powertrain'].items():
report += f"• **{data['param_name']}:** {data['value']}\n"
report += "\n"
# TRANSMISSION
if detailed_info['transmission']:
report += "⚙️ **TRANSMISSION**\n"
for key, data in detailed_info['transmission'].items():
report += f"• **{data['param_name']}:** {data['value']}\n"
report += "\n"
# ACTIVE SAFETY
if detailed_info['active_safety']:
report += "🛡️ **ACTIVE SAFETY**\n"
for key, data in detailed_info['active_safety'].items():
report += f"• **{data['param_name']}:** {data['value']}\n"
report += "\n"
# PASSIVE SAFETY
if detailed_info['passive_safety']:
report += "🚗 **PASSIVE SAFETY**\n"
for key, data in detailed_info['passive_safety'].items():
report += f"• **{data['param_name']}:** {data['value']}\n"
report += "\n"
# DIMENSIONS AND CONSTRUCTION
if detailed_info['dimensions_and_construction']:
report += "📏 **DIMENSIONS AND CONSTRUCTION**\n"
for key, data in detailed_info['dimensions_and_construction'].items():
report += f"• **{data['param_name']}:** {data['value']}\n"
report += "\n"
# BRAKE SYSTEM
if detailed_info['brake_system']:
report += "🔧 **BRAKE SYSTEM**\n"
for key, data in detailed_info['brake_system'].items():
report += f"• **{data['param_name']}:** {data['value']}\n"
report += "\n"
# LIGHTING
if detailed_info['lighting']:
report += "💡 **LIGHTING**\n"
for key, data in detailed_info['lighting'].items():
report += f"• **{data['param_name']}:** {data['value']}\n"
report += "\n"
# ADDITIONAL FEATURES
if detailed_info['additional_features']:
report += "✨ **ADDITIONAL FEATURES**\n"
for key, data in detailed_info['additional_features'].items():
report += f"• **{data['param_name']}:** {data['value']}\n"
report += "\n"
# MANUFACTURING AND LOCALIZATION
if detailed_info['manufacturing_and_localization']:
report += "🏭 **MANUFACTURING AND LOCALIZATION**\n"
for key, data in detailed_info['manufacturing_and_localization'].items():
report += f"• **{data['param_name']}:** {data['value']}\n"
report += "\n"
# NCSA DATA
if detailed_info['ncsa_data']:
report += "📊 **NCSA DATA**\n"
for key, data in detailed_info['ncsa_data'].items():
report += f"• **{data['param_name']}:** {data['value']}\n"
report += "\n"
# TECHNICAL INFORMATION AND ERRORS
if detailed_info['technical_information_and_errors']:
report += "⚠️ **TECHNICAL INFORMATION AND ERRORS**\n"
for key, data in detailed_info['technical_information_and_errors'].items():
if data['value'] == "0":
report += "✅ **No errors found**\n"
else:
report += f"• **{data['param_name']}:** {data['value']}\n"
report += "\n"
report += "---\n"
report += f"📋 **VIN:** {vin}\n"
report += f"💰 **Transaction ID:** {message.successful_payment.telegram_payment_charge_id}"
# Create keyboard with action buttons
builder = InlineKeyboardBuilder()
builder.button(text="Try another VIN", callback_data="decode_vin")
builder.button(text="Back to Main Menu", callback_data="main_menu")
builder.adjust(2) # Two buttons on one row
await message.answer(report, reply_markup=builder.as_markup(), parse_mode="Markdown")
else:
# No detailed information found - refund the payment
try:
await message.bot.refund_star_payment(
user_id=message.from_user.id,
telegram_payment_charge_id=message.successful_payment.telegram_payment_charge_id
)
await message.answer(
"❌ No detailed information found for this VIN in our database.\n"
"💰 Your payment has been automatically refunded.\n"
"Please verify the VIN and try again."
)
logging.info(f"Refund successful for user {message.from_user.id} - no data found for VIN {vin}")
except Exception as refund_error:
logging.error(f"Failed to refund payment for user {message.from_user.id}: {refund_error}")
await message.answer(
"❌ No detailed information found for this VIN.\n"
"⚠️ Please contact support with this transaction ID for a refund:\n"
f"🆔 {message.successful_payment.telegram_payment_charge_id}"
)
except Exception as e:
logging.error(f"Error getting detailed VIN info for {vin}: {e}")
# Attempt to refund the payment due to service error
try:
await message.bot.refund_star_payment(
user_id=message.from_user.id,
telegram_payment_charge_id=message.successful_payment.telegram_payment_charge_id
)
await message.answer(
"❌ Error retrieving detailed information from our database.\n"
"💰 Your payment has been automatically refunded.\n"
"Please try again later or contact support if the issue persists."
)
logging.info(f"Refund successful for user {message.from_user.id}, charge_id: {message.successful_payment.telegram_payment_charge_id}")
except Exception as refund_error:
logging.error(f"Failed to refund payment for user {message.from_user.id}: {refund_error}")
await message.answer(
"❌ Error retrieving detailed information from our database.\n"
"⚠️ Please contact support immediately with this transaction ID for a manual refund:\n"
f"🆔 {message.successful_payment.telegram_payment_charge_id}"
)
elif payload.startswith("detailed_salvage_check:"):
vin = payload.split(":")[1]
try:
# Получаем детальную информацию о salvage записях
salvage_records = await database.fetch_salvage_detailed_info(vin)
if salvage_records:
# Получаем базовую информацию о VIN для заголовка
make, model, year, cnt = await database.fetch_vin_info(vin)
report = f"🚗 **{year} {make} {model}**\n"
report += f"📋 **VIN:** {vin}\n\n"
report += f"🔍 **DETAILED SALVAGE HISTORY REPORT**\n"
report += f"📊 **Total Records Found:** {len(salvage_records)}\n\n"
# Добавляем информацию по каждой записи
for idx, record in enumerate(salvage_records[:5], 1): # Показываем максимум 5 записей
report += f"📋 **Record #{idx}**\n"
if record['sale_date']:
report += f"📅 **Sale Date:** {record['sale_date']}\n"
if record['primary_damage']:
report += f"⚠️ **Primary Damage:** {record['primary_damage']}\n"
if record['secondary_damage']:
report += f"⚠️ **Secondary Damage:** {record['secondary_damage']}\n"
if record['vehicle_type']:
report += f"🚙 **Vehicle Type:** {record['vehicle_type']}\n"
if record['odometer']:
report += f"🛣️ **Odometer:** {record['odometer']:,} miles\n"
if record['sale_title_state']:
report += f"📍 **Title State:** {record['sale_title_state']}\n"
if record['sale_title_type']:
report += f"📄 **Title Type:** {record['sale_title_type']}\n"
if record['seller']:
report += f"🏢 **Seller:** {record['seller']}\n"
if record['estimate_repair_cost']:
report += f"💰 **Estimated Repair Cost:** ${record['estimate_repair_cost']:,}\n"
if record['actual_cash_value']:
report += f"💵 **Actual Cash Value:** ${record['actual_cash_value']:,}\n"
if record['sale_location']:
report += f"📍 **Sale Location:** {record['sale_location']}\n"
report += "\n"
if len(salvage_records) > 5:
report += f"📋 **... and {len(salvage_records) - 5} more records**\n\n"
report += "---\n"
report += f"💰 **Transaction ID:** {message.successful_payment.telegram_payment_charge_id}\n"
report += "⚠️ **This report shows salvage/damage history. Please consult with automotive experts for vehicle evaluation.**"
# Создаем клавиатуру
builder = InlineKeyboardBuilder()
builder.button(text="Try another VIN", callback_data="check_vin")
builder.button(text="Back to Main Menu", callback_data="main_menu")
builder.adjust(2)
await message.answer(report, reply_markup=builder.as_markup(), parse_mode="Markdown")
else:
# Нет записей - возвращаем деньги
try:
await message.bot.refund_star_payment(
user_id=message.from_user.id,
telegram_payment_charge_id=message.successful_payment.telegram_payment_charge_id
)
await message.answer(
"❌ No salvage records found for this VIN in our database.\n"
"💰 Your payment has been automatically refunded.\n"
"This is actually good news - no salvage history found!"
)
logging.info(f"Refund successful for user {message.from_user.id} - no salvage data found for VIN {vin}")
except Exception as refund_error:
logging.error(f"Failed to refund payment for user {message.from_user.id}: {refund_error}")
await message.answer(
"❌ No salvage records found for this VIN.\n"
"⚠️ Please contact support with this transaction ID for a refund:\n"
f"🆔 {message.successful_payment.telegram_payment_charge_id}"
)
except Exception as e:
logging.error(f"Error getting salvage info for {vin}: {e}")
# Возвращаем деньги при ошибке
try:
await message.bot.refund_star_payment(
user_id=message.from_user.id,
telegram_payment_charge_id=message.successful_payment.telegram_payment_charge_id
)
await message.answer(
"❌ Error retrieving salvage information from our database.\n"
"💰 Your payment has been automatically refunded.\n"
"Please try again later or contact support if the issue persists."
)
logging.info(f"Refund successful for user {message.from_user.id}, charge_id: {message.successful_payment.telegram_payment_charge_id}")
except Exception as refund_error:
logging.error(f"Failed to refund payment for user {message.from_user.id}: {refund_error}")
await message.answer(
"❌ Error retrieving salvage information from our database.\n"
"⚠️ Please contact support immediately with this transaction ID for a manual refund:\n"
f"🆔 {message.successful_payment.telegram_payment_charge_id}"
)
else:
await message.answer(
f"✅ Payment successful! Thank you for your purchase.\n"
f"Transaction ID: {message.successful_payment.telegram_payment_charge_id}"
)
async def on_startup():
await oracle_db.connect()
# Регистрируем middleware для всех типов событий
dp.message.middleware(DbSessionMiddleware(oracle_db))
dp.callback_query.middleware(DbSessionMiddleware(oracle_db))
dp.pre_checkout_query.middleware(DbSessionMiddleware(oracle_db))
async def on_shutdown():
await oracle_db.close()
# Run the bot
async def main() -> None:
bot = Bot(token=TOKEN)
dp.startup.register(on_startup)
dp.shutdown.register(on_shutdown)
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())