Добавлены функции для получения путей к фотографиям и подсчета их количества по VIN в классе OracleDatabase. Обновлены обработчики в main.py для обработки запросов на получение фотографий, включая логику оплаты и отправки изображений пользователю. Эти изменения улучшают функциональность бота и позволяют пользователям получать доступ к фотографиям повреждений автомобилей.
This commit is contained in:
parent
3474fe5f96
commit
5f3d478adb
26
db.py
26
db.py
@ -127,6 +127,19 @@ class OracleDatabase:
|
|||||||
import asyncio
|
import asyncio
|
||||||
return await asyncio.to_thread(_query)
|
return await asyncio.to_thread(_query)
|
||||||
|
|
||||||
|
async def fetch_photo_paths(self, vin: str) -> list:
|
||||||
|
"""
|
||||||
|
Получает список путей к фотографиям для данного VIN
|
||||||
|
"""
|
||||||
|
def _query():
|
||||||
|
with self._pool.acquire() as conn:
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute("SELECT ipath FROM salvagedb.salvage_images WHERE fn = 1 AND vin = :vin", {"vin": vin})
|
||||||
|
results = cur.fetchall()
|
||||||
|
return [row[0] for row in results if row[0]] if results else []
|
||||||
|
import asyncio
|
||||||
|
return await asyncio.to_thread(_query)
|
||||||
|
|
||||||
async def fetch_detailed_vin_info(self, vin: str) -> dict:
|
async def fetch_detailed_vin_info(self, vin: str) -> dict:
|
||||||
# Manual async wrapper since oracledb is synchronous (threaded)
|
# Manual async wrapper since oracledb is synchronous (threaded)
|
||||||
def _query():
|
def _query():
|
||||||
@ -378,3 +391,16 @@ class OracleDatabase:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error getting users summary: {e}")
|
print(f"Error getting users summary: {e}")
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
async def count_photo_records(self, vin: str) -> int:
|
||||||
|
"""
|
||||||
|
Подсчитывает количество фотографий для данного VIN
|
||||||
|
"""
|
||||||
|
def _query():
|
||||||
|
with self._pool.acquire() as conn:
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute("SELECT COUNT(*) FROM salvagedb.salvage_images WHERE vin = :vin AND fn = 1", {"vin": vin})
|
||||||
|
result = cur.fetchone()
|
||||||
|
return result[0] if result else 0
|
||||||
|
import asyncio
|
||||||
|
return await asyncio.to_thread(_query)
|
||||||
|
|||||||
77
db_sql/salvage.sql
Normal file
77
db_sql/salvage.sql
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
-- Create table
|
||||||
|
create table SALVAGEDB
|
||||||
|
(
|
||||||
|
num NUMBER(20),
|
||||||
|
vin VARCHAR2(20),
|
||||||
|
svin VARCHAR2(15),
|
||||||
|
odo NUMBER(11),
|
||||||
|
odos VARCHAR2(50),
|
||||||
|
title VARCHAR2(200),
|
||||||
|
dem1 VARCHAR2(200),
|
||||||
|
dem2 VARCHAR2(200),
|
||||||
|
year NUMBER(4),
|
||||||
|
month NUMBER(4),
|
||||||
|
last_update DATE,
|
||||||
|
auction VARCHAR2(1)
|
||||||
|
)
|
||||||
|
tablespace USERS
|
||||||
|
pctfree 10
|
||||||
|
initrans 1
|
||||||
|
maxtrans 255
|
||||||
|
storage
|
||||||
|
(
|
||||||
|
initial 3581M
|
||||||
|
next 1M
|
||||||
|
minextents 1
|
||||||
|
maxextents unlimited
|
||||||
|
)
|
||||||
|
compress;
|
||||||
|
-- Add comments to the columns
|
||||||
|
comment on column SALVAGEDB.num
|
||||||
|
is 'ID';
|
||||||
|
comment on column SALVAGEDB.vin
|
||||||
|
is 'vin';
|
||||||
|
comment on column SALVAGEDB.odo
|
||||||
|
is 'îäîìåòð';
|
||||||
|
comment on column SALVAGEDB.odos
|
||||||
|
is 'îäîìåòð ñòàòóñ';
|
||||||
|
comment on column SALVAGEDB.title
|
||||||
|
is 'äîêóìåíò';
|
||||||
|
comment on column SALVAGEDB.dem1
|
||||||
|
is 'ïîâðåäåëíèå 1';
|
||||||
|
comment on column SALVAGEDB.dem2
|
||||||
|
is 'ïîâðåæåäíèå 2';
|
||||||
|
comment on column SALVAGEDB.year
|
||||||
|
is 'ìåñÿö';
|
||||||
|
comment on column SALVAGEDB.month
|
||||||
|
is 'ãîä';
|
||||||
|
comment on column SALVAGEDB.auction
|
||||||
|
is 'Ñ êîïàðò I - IAAI';
|
||||||
|
-- Create/Recreate indexes
|
||||||
|
create index IDX_SALVAGEDB_NUM on SALVAGEDB (NUM)
|
||||||
|
tablespace USERS
|
||||||
|
pctfree 10
|
||||||
|
initrans 2
|
||||||
|
maxtrans 255
|
||||||
|
storage
|
||||||
|
(
|
||||||
|
initial 64K
|
||||||
|
next 1M
|
||||||
|
minextents 1
|
||||||
|
maxextents unlimited
|
||||||
|
)
|
||||||
|
compress nologging;
|
||||||
|
create index IDX_SALVAGEDB_SVIN_VIN on SALVAGEDB (SVIN, VIN)
|
||||||
|
tablespace USERS
|
||||||
|
pctfree 10
|
||||||
|
initrans 2
|
||||||
|
maxtrans 255
|
||||||
|
storage
|
||||||
|
(
|
||||||
|
initial 64K
|
||||||
|
next 1M
|
||||||
|
minextents 1
|
||||||
|
maxextents unlimited
|
||||||
|
);
|
||||||
|
-- Grant/Revoke object privileges
|
||||||
|
grant read on SALVAGEDB to SALVAGEBOT;
|
||||||
36
db_sql/salvage_images.sql
Normal file
36
db_sql/salvage_images.sql
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
-- Create table
|
||||||
|
create table SALVAGE_IMAGES
|
||||||
|
(
|
||||||
|
vin VARCHAR2(20) not null,
|
||||||
|
dateadd TIMESTAMP(6) not null,
|
||||||
|
ipath VARCHAR2(500) not null,
|
||||||
|
fn NUMBER(1) default 0 not null
|
||||||
|
)
|
||||||
|
tablespace USERS
|
||||||
|
pctfree 0
|
||||||
|
initrans 1
|
||||||
|
maxtrans 255
|
||||||
|
storage
|
||||||
|
(
|
||||||
|
initial 64K
|
||||||
|
next 1M
|
||||||
|
minextents 1
|
||||||
|
maxextents unlimited
|
||||||
|
)
|
||||||
|
compress;
|
||||||
|
-- Create/Recreate indexes
|
||||||
|
create unique index VIB_DATEADD_IDX on SALVAGE_IMAGES (VIN, DATEADD)
|
||||||
|
tablespace USERS
|
||||||
|
pctfree 10
|
||||||
|
initrans 2
|
||||||
|
maxtrans 255
|
||||||
|
storage
|
||||||
|
(
|
||||||
|
initial 64K
|
||||||
|
next 1M
|
||||||
|
minextents 1
|
||||||
|
maxextents unlimited
|
||||||
|
)
|
||||||
|
compress 1;
|
||||||
|
-- Grant/Revoke object privileges
|
||||||
|
grant read on SALVAGE_IMAGES to SALVAGEBOT;
|
||||||
413
main.py
413
main.py
@ -7,7 +7,7 @@ import os
|
|||||||
|
|
||||||
from aiogram import Bot, Dispatcher
|
from aiogram import Bot, Dispatcher
|
||||||
from aiogram.filters import Command
|
from aiogram.filters import Command
|
||||||
from aiogram.types import Message, InlineKeyboardButton, InlineKeyboardMarkup, CallbackQuery, LabeledPrice, PreCheckoutQuery
|
from aiogram.types import Message, InlineKeyboardButton, InlineKeyboardMarkup, CallbackQuery, LabeledPrice, PreCheckoutQuery, InputMediaPhoto, FSInputFile
|
||||||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||||||
from aiogram.fsm.state import State, StatesGroup
|
from aiogram.fsm.state import State, StatesGroup
|
||||||
from aiogram.fsm.context import FSMContext
|
from aiogram.fsm.context import FSMContext
|
||||||
@ -148,6 +148,165 @@ def is_macos() -> bool:
|
|||||||
return get_operating_system() == 'macOS'
|
return get_operating_system() == 'macOS'
|
||||||
|
|
||||||
|
|
||||||
|
def convert_photo_path(db_path: str) -> str:
|
||||||
|
"""
|
||||||
|
Конвертирует путь к фотографии в зависимости от операционной системы
|
||||||
|
Args:
|
||||||
|
db_path: путь из базы данных в Linux формате (с /)
|
||||||
|
Returns:
|
||||||
|
str: полный путь к файлу с учетом OS и базового пути
|
||||||
|
"""
|
||||||
|
if not db_path:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
# Базовый путь из константы
|
||||||
|
base_path = image_path
|
||||||
|
|
||||||
|
if is_windows():
|
||||||
|
# Конвертируем Linux пути в Windows формат
|
||||||
|
windows_path = db_path.replace('/', '\\')
|
||||||
|
full_path = f"{base_path}\\{windows_path}"
|
||||||
|
logging.info(f"Converted path for Windows: {db_path} -> {full_path}")
|
||||||
|
return full_path
|
||||||
|
else:
|
||||||
|
# Для Linux/macOS оставляем как есть
|
||||||
|
full_path = f"{base_path}/{db_path}"
|
||||||
|
logging.info(f"Path for Linux/macOS: {db_path} -> {full_path}")
|
||||||
|
return full_path
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_photo_paths(db_paths: list) -> list:
|
||||||
|
"""
|
||||||
|
Подготавливает список полных путей к фотографиям
|
||||||
|
Args:
|
||||||
|
db_paths: список путей из базы данных
|
||||||
|
Returns:
|
||||||
|
list: список полных путей к файлам
|
||||||
|
"""
|
||||||
|
if not db_paths:
|
||||||
|
return []
|
||||||
|
|
||||||
|
full_paths = []
|
||||||
|
for db_path in db_paths:
|
||||||
|
full_path = convert_photo_path(db_path)
|
||||||
|
if full_path:
|
||||||
|
full_paths.append(full_path)
|
||||||
|
|
||||||
|
logging.info(f"Prepared {len(full_paths)} photo paths from {len(db_paths)} database paths")
|
||||||
|
return full_paths
|
||||||
|
|
||||||
|
|
||||||
|
async def send_vehicle_photos(message: Message, vin: str, photo_paths: list, make: str, model: str, year: str):
|
||||||
|
"""
|
||||||
|
Отправляет фотографии автомобиля пользователю
|
||||||
|
Args:
|
||||||
|
message: сообщение пользователя
|
||||||
|
vin: VIN автомобиля
|
||||||
|
photo_paths: список полных путей к фотографиям
|
||||||
|
make, model, year: информация об автомобиле
|
||||||
|
"""
|
||||||
|
if not photo_paths:
|
||||||
|
await message.answer("❌ No photos found to send.")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Telegram позволяет максимум 10 фотографий в media group
|
||||||
|
photos_per_group = 10
|
||||||
|
total_photos = len(photo_paths)
|
||||||
|
|
||||||
|
logging.info(f"Attempting to send {total_photos} photos for VIN: {vin}")
|
||||||
|
|
||||||
|
# Разбиваем фотографии на группы по 10
|
||||||
|
photo_groups = [photo_paths[i:i + photos_per_group] for i in range(0, len(photo_paths), photos_per_group)]
|
||||||
|
total_groups = len(photo_groups)
|
||||||
|
|
||||||
|
logging.info(f"Split {total_photos} photos into {total_groups} groups")
|
||||||
|
|
||||||
|
sent_count = 0
|
||||||
|
|
||||||
|
for group_num, photo_group in enumerate(photo_groups, 1):
|
||||||
|
try:
|
||||||
|
logging.info(f"Processing group {group_num}/{total_groups} with {len(photo_group)} photos")
|
||||||
|
|
||||||
|
# Создаем media group для текущей группы
|
||||||
|
media_group = []
|
||||||
|
|
||||||
|
for i, photo_path in enumerate(photo_group):
|
||||||
|
try:
|
||||||
|
# Проверяем существование файла
|
||||||
|
if os.path.exists(photo_path):
|
||||||
|
# Создаем InputMediaPhoto
|
||||||
|
if i == 0 and group_num == 1:
|
||||||
|
# Первая фотография первой группы с полным описанием
|
||||||
|
if make == "UNKNOWN" and model == "UNKNOWN" and year == "UNKNOWN":
|
||||||
|
caption = f"📸 Vehicle damage photos\n📋 VIN: {vin}\n📊 Total photos: {total_photos}\n🗂️ Group {group_num}/{total_groups}"
|
||||||
|
else:
|
||||||
|
caption = f"📸 {year} {make} {model}\n📋 VIN: {vin}\n📊 Total photos: {total_photos}\n🗂️ Group {group_num}/{total_groups}"
|
||||||
|
elif i == 0:
|
||||||
|
# Первая фотография других групп с номером группы
|
||||||
|
caption = f"🗂️ Group {group_num}/{total_groups}"
|
||||||
|
else:
|
||||||
|
# Остальные фотографии без описания
|
||||||
|
caption = None
|
||||||
|
|
||||||
|
if caption:
|
||||||
|
media_group.append(InputMediaPhoto(
|
||||||
|
media=FSInputFile(photo_path),
|
||||||
|
caption=caption
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
media_group.append(InputMediaPhoto(
|
||||||
|
media=FSInputFile(photo_path)
|
||||||
|
))
|
||||||
|
|
||||||
|
sent_count += 1
|
||||||
|
logging.info(f"Added photo {sent_count}/{total_photos}: {photo_path}")
|
||||||
|
else:
|
||||||
|
logging.warning(f"Photo file not found: {photo_path}")
|
||||||
|
except Exception as photo_error:
|
||||||
|
logging.error(f"Error processing photo {sent_count + 1} ({photo_path}): {photo_error}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if media_group:
|
||||||
|
# Отправляем media group
|
||||||
|
await message.answer_media_group(media_group)
|
||||||
|
logging.info(f"Successfully sent group {group_num}/{total_groups} with {len(media_group)} photos")
|
||||||
|
|
||||||
|
# Небольшая пауза между группами для избежания rate limiting
|
||||||
|
if group_num < total_groups:
|
||||||
|
await asyncio.sleep(0.5)
|
||||||
|
else:
|
||||||
|
logging.warning(f"No valid photos in group {group_num}")
|
||||||
|
|
||||||
|
except Exception as group_error:
|
||||||
|
logging.error(f"Error sending photo group {group_num}: {group_error}")
|
||||||
|
await message.answer(f"❌ Error sending photo group {group_num}. Continuing with remaining photos...")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if sent_count > 0:
|
||||||
|
# Отправляем итоговое сообщение
|
||||||
|
await message.answer(
|
||||||
|
f"✅ **Photos sent successfully!**\n"
|
||||||
|
f"📊 **{sent_count} of {total_photos} photos** delivered\n"
|
||||||
|
f"🗂️ **{total_groups} photo groups** sent"
|
||||||
|
)
|
||||||
|
logging.info(f"Successfully sent {sent_count}/{total_photos} photos for VIN: {vin}")
|
||||||
|
else:
|
||||||
|
# Если ни одна фотография не найдена
|
||||||
|
await message.answer(
|
||||||
|
"❌ **Error:** Photo files not found on server.\n"
|
||||||
|
"Please contact support with your transaction details."
|
||||||
|
)
|
||||||
|
logging.error(f"No valid photo files found for VIN: {vin}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error sending photos for VIN {vin}: {e}")
|
||||||
|
await message.answer(
|
||||||
|
"❌ **Error sending photos.** Please contact support with your transaction details.\n"
|
||||||
|
f"Error details: {str(e)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if getenv("DEBUG",'0') == '1':
|
if getenv("DEBUG",'0') == '1':
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
else:
|
else:
|
||||||
@ -182,6 +341,7 @@ dp = Dispatcher()
|
|||||||
class VinStates(StatesGroup):
|
class VinStates(StatesGroup):
|
||||||
waiting_for_vin = State()
|
waiting_for_vin = State()
|
||||||
waiting_for_check_vin = State()
|
waiting_for_check_vin = State()
|
||||||
|
waiting_for_photo_vin = State()
|
||||||
|
|
||||||
|
|
||||||
# Command handler
|
# Command handler
|
||||||
@ -240,6 +400,19 @@ async def check_vin_callback(callback: CallbackQuery, state: FSMContext, db: Ora
|
|||||||
await callback.answer()
|
await callback.answer()
|
||||||
|
|
||||||
|
|
||||||
|
@dp.callback_query(lambda c: c.data == "search_car_photo")
|
||||||
|
async def search_car_photo_callback(callback: CallbackQuery, state: FSMContext, db: OracleDatabase = None):
|
||||||
|
# Используем переданный db или глобальный oracle_db
|
||||||
|
database = db or oracle_db
|
||||||
|
|
||||||
|
# Сохраняем данные пользователя при нажатии кнопки
|
||||||
|
await database.save_user(callback.from_user, "search_car_photo_button")
|
||||||
|
|
||||||
|
await callback.message.answer("Please enter the vehicle VIN to search for damage photos.")
|
||||||
|
await state.set_state(VinStates.waiting_for_photo_vin)
|
||||||
|
await callback.answer()
|
||||||
|
|
||||||
|
|
||||||
@dp.callback_query(lambda c: c.data == "main_menu")
|
@dp.callback_query(lambda c: c.data == "main_menu")
|
||||||
async def main_menu_callback(callback: CallbackQuery, state: FSMContext, db: OracleDatabase = None):
|
async def main_menu_callback(callback: CallbackQuery, state: FSMContext, db: OracleDatabase = None):
|
||||||
# Используем переданный db или глобальный oracle_db
|
# Используем переданный db или глобальный oracle_db
|
||||||
@ -412,6 +585,31 @@ async def pay_check_detailed_callback(callback: CallbackQuery, db: OracleDatabas
|
|||||||
await callback.answer()
|
await callback.answer()
|
||||||
|
|
||||||
|
|
||||||
|
@dp.callback_query(lambda c: c.data and c.data.startswith("pay_photos:"))
|
||||||
|
async def pay_photos_callback(callback: CallbackQuery, db: OracleDatabase = None):
|
||||||
|
# Используем переданный db или глобальный oracle_db
|
||||||
|
database = db or oracle_db
|
||||||
|
|
||||||
|
# Сохраняем данные пользователя при инициации платежа
|
||||||
|
await database.save_user(callback.from_user, "photos_payment_initiation")
|
||||||
|
|
||||||
|
# Извлекаем VIN из callback data
|
||||||
|
vin = callback.data.split(":")[1]
|
||||||
|
prices = [LabeledPrice(label="Vehicle Damage Photos", amount=IMG_PRICE)]
|
||||||
|
logging.info(f"Sending invoice for photos VIN: {vin}")
|
||||||
|
|
||||||
|
await callback.bot.send_invoice(
|
||||||
|
chat_id=callback.message.chat.id,
|
||||||
|
title="Vehicle Damage Photos",
|
||||||
|
description=f"Get access to all damage photos for this vehicle for {IMG_PRICE} Telegram Stars",
|
||||||
|
payload=f"vehicle_photos:{vin}", # Уникальный payload для фотографий
|
||||||
|
provider_token="", # Empty for Telegram Stars
|
||||||
|
currency="XTR", # Telegram Stars currency
|
||||||
|
prices=prices
|
||||||
|
)
|
||||||
|
await callback.answer()
|
||||||
|
|
||||||
|
|
||||||
@dp.pre_checkout_query()
|
@dp.pre_checkout_query()
|
||||||
async def pre_checkout_handler(pre_checkout_query: PreCheckoutQuery, db: OracleDatabase = None):
|
async def pre_checkout_handler(pre_checkout_query: PreCheckoutQuery, db: OracleDatabase = None):
|
||||||
# Используем переданный db или глобальный oracle_db
|
# Используем переданный db или глобальный oracle_db
|
||||||
@ -475,7 +673,13 @@ async def successful_payment_handler(message: Message, db: OracleDatabase = None
|
|||||||
payload = message.successful_payment.invoice_payload
|
payload = message.successful_payment.invoice_payload
|
||||||
|
|
||||||
# Определяем сумму платежа в зависимости от типа
|
# Определяем сумму платежа в зависимости от типа
|
||||||
payment_amount = 10.0 if payload.startswith("detailed_salvage_check:") else 1.0
|
if payload.startswith("detailed_salvage_check:"):
|
||||||
|
payment_amount = float(CHECK_PRICE)
|
||||||
|
elif payload.startswith("vehicle_photos:"):
|
||||||
|
payment_amount = float(IMG_PRICE)
|
||||||
|
else:
|
||||||
|
payment_amount = float(DECODE_PRICE)
|
||||||
|
|
||||||
await database.update_user_payment(message.from_user.id, payment_amount)
|
await database.update_user_payment(message.from_user.id, payment_amount)
|
||||||
|
|
||||||
if payload.startswith("detailed_vin_info:"):
|
if payload.startswith("detailed_vin_info:"):
|
||||||
@ -861,6 +1065,151 @@ async def successful_payment_handler(message: Message, db: OracleDatabase = None
|
|||||||
"⚠️ Please contact support immediately with this transaction ID for a manual refund:\n"
|
"⚠️ Please contact support immediately with this transaction ID for a manual refund:\n"
|
||||||
f"🆔 {message.successful_payment.telegram_payment_charge_id}"
|
f"🆔 {message.successful_payment.telegram_payment_charge_id}"
|
||||||
)
|
)
|
||||||
|
elif payload.startswith("vehicle_photos:"):
|
||||||
|
vin = payload.split(":")[1]
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Получаем информацию о VIN и количество фотографий
|
||||||
|
make, model, year, cnt = await database.fetch_vin_info(vin)
|
||||||
|
photo_count = await database.count_photo_records(vin)
|
||||||
|
|
||||||
|
logging.info(f"Photos payment for VIN: {vin}, make: {make}, model: {model}, year: {year}, photo_count: {photo_count}")
|
||||||
|
|
||||||
|
if photo_count > 0:
|
||||||
|
# Есть фотографии - предоставляем доступ (пока заглушка)
|
||||||
|
if make == "UNKNOWN" and model == "UNKNOWN" and year == "UNKNOWN":
|
||||||
|
response_text = f"📸 **Vehicle Damage Photos**\n\n"
|
||||||
|
else:
|
||||||
|
response_text = f"🚗 **{year} {make} {model}**\n\n"
|
||||||
|
response_text += f"📸 **Vehicle Damage Photos**\n\n"
|
||||||
|
|
||||||
|
response_text += f"✅ **Payment successful!** You now have access to **{photo_count} damage photos** for this vehicle.\n\n"
|
||||||
|
response_text += f"📁 **Photos will be sent separately** - please wait while we prepare your images.\n\n"
|
||||||
|
response_text += f"---\n"
|
||||||
|
response_text += f"💰 **Transaction ID:** {escape_markdown(message.successful_payment.telegram_payment_charge_id)}\n"
|
||||||
|
response_text += f"📋 **VIN:** {escape_markdown(vin)}"
|
||||||
|
|
||||||
|
# Создаем клавиатуру с дополнительными действиями
|
||||||
|
builder = InlineKeyboardBuilder()
|
||||||
|
builder.button(text="Search another VIN", callback_data="search_car_photo")
|
||||||
|
builder.button(text="Back to Main Menu", callback_data="main_menu")
|
||||||
|
builder.adjust(2)
|
||||||
|
|
||||||
|
logging.info("Attempting to send photos payment success message...")
|
||||||
|
try:
|
||||||
|
await message.answer(response_text, reply_markup=builder.as_markup(), parse_mode="Markdown")
|
||||||
|
logging.info("Photos payment message sent successfully!")
|
||||||
|
|
||||||
|
# Получаем пути к фотографиям из базы данных
|
||||||
|
logging.info(f"Fetching photo paths for VIN: {vin}")
|
||||||
|
db_photo_paths = await database.fetch_photo_paths(vin)
|
||||||
|
logging.info(f"Found {len(db_photo_paths)} photo paths in database")
|
||||||
|
|
||||||
|
if db_photo_paths:
|
||||||
|
# Подготавливаем полные пути к файлам
|
||||||
|
full_photo_paths = prepare_photo_paths(db_photo_paths)
|
||||||
|
logging.info(f"Prepared {len(full_photo_paths)} full photo paths")
|
||||||
|
|
||||||
|
# Отправляем фотографии
|
||||||
|
await send_vehicle_photos(message, vin, full_photo_paths, make, model, year)
|
||||||
|
else:
|
||||||
|
await message.answer(
|
||||||
|
"⚠️ **Warning:** No photo paths found in database despite photo count > 0.\n"
|
||||||
|
"Please contact support with your transaction details."
|
||||||
|
)
|
||||||
|
logging.warning(f"No photo paths found for VIN {vin} despite photo_count = {photo_count}")
|
||||||
|
|
||||||
|
except Exception as markdown_error:
|
||||||
|
logging.error(f"Markdown parsing failed for photos payment: {markdown_error}")
|
||||||
|
plain_response = response_text.replace("**", "").replace("*", "")
|
||||||
|
await message.answer(plain_response, reply_markup=builder.as_markup())
|
||||||
|
logging.info("Plain text photos payment message sent successfully!")
|
||||||
|
|
||||||
|
# Получаем пути к фотографиям из базы данных (fallback)
|
||||||
|
logging.info(f"Fetching photo paths for VIN: {vin} (fallback)")
|
||||||
|
db_photo_paths = await database.fetch_photo_paths(vin)
|
||||||
|
logging.info(f"Found {len(db_photo_paths)} photo paths in database (fallback)")
|
||||||
|
|
||||||
|
if db_photo_paths:
|
||||||
|
# Подготавливаем полные пути к файлам
|
||||||
|
full_photo_paths = prepare_photo_paths(db_photo_paths)
|
||||||
|
logging.info(f"Prepared {len(full_photo_paths)} full photo paths (fallback)")
|
||||||
|
|
||||||
|
# Отправляем фотографии
|
||||||
|
await send_vehicle_photos(message, vin, full_photo_paths, make, model, year)
|
||||||
|
else:
|
||||||
|
await message.answer(
|
||||||
|
"Warning: No photo paths found in database despite photo count > 0. "
|
||||||
|
"Please contact support with your transaction details."
|
||||||
|
)
|
||||||
|
logging.warning(f"No photo paths found for VIN {vin} despite photo_count = {photo_count} (fallback)")
|
||||||
|
|
||||||
|
# Проверяем, является ли пользователь администратором и возвращаем звезды
|
||||||
|
if message.from_user.id == ADMIN_USER_ID:
|
||||||
|
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(
|
||||||
|
"🔧 **Admin Refund**\n\n"
|
||||||
|
f"💰 Payment automatically refunded for admin user.\n"
|
||||||
|
f"🆔 Transaction ID: {escape_markdown(message.successful_payment.telegram_payment_charge_id)}\n"
|
||||||
|
"ℹ️ Admin access - no charges applied.",
|
||||||
|
parse_mode="Markdown"
|
||||||
|
)
|
||||||
|
logging.info(f"Admin refund successful for user {message.from_user.id}")
|
||||||
|
except Exception as refund_error:
|
||||||
|
logging.error(f"Failed to refund admin payment: {refund_error}")
|
||||||
|
await message.answer(
|
||||||
|
"⚠️ **Admin Refund Failed**\n\n"
|
||||||
|
"Could not automatically refund admin payment. Please contact technical support.\n"
|
||||||
|
f"🆔 Transaction ID: {escape_markdown(message.successful_payment.telegram_payment_charge_id)}",
|
||||||
|
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 photos found for this VIN in our database.\n"
|
||||||
|
"💰 Your payment has been automatically refunded.\n"
|
||||||
|
"Please try another VIN or contact support if you believe this is an error."
|
||||||
|
)
|
||||||
|
logging.info(f"Refund successful for user {message.from_user.id} - no photos 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 photos 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 photos 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 photos 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 photos 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:
|
else:
|
||||||
await message.answer(
|
await message.answer(
|
||||||
f"✅ Payment successful! Thank you for your purchase.\n"
|
f"✅ Payment successful! Thank you for your purchase.\n"
|
||||||
@ -892,6 +1241,66 @@ async def successful_payment_handler(message: Message, db: OracleDatabase = None
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message(VinStates.waiting_for_photo_vin)
|
||||||
|
async def process_photo_vin(message: Message, state: FSMContext, db: OracleDatabase = None):
|
||||||
|
# Используем переданный db или глобальный oracle_db
|
||||||
|
database = db or oracle_db
|
||||||
|
|
||||||
|
# Сохраняем данные пользователя при обработке VIN
|
||||||
|
await database.save_user(message.from_user, "photo_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)
|
||||||
|
|
||||||
|
# Получаем количество фотографий
|
||||||
|
photo_count = await database.count_photo_records(vin)
|
||||||
|
|
||||||
|
logging.info(f"Photo search VIN: make: {make}, model: {model}, year: {year}, photo_count: {photo_count}")
|
||||||
|
|
||||||
|
# Формируем ответ в зависимости от наличия фотографий
|
||||||
|
builder = InlineKeyboardBuilder()
|
||||||
|
|
||||||
|
if photo_count > 0:
|
||||||
|
# Есть фотографии - показываем информацию и кнопку оплаты
|
||||||
|
if make == "UNKNOWN" and model == "UNKNOWN" and year == "UNKNOWN":
|
||||||
|
response_text = f"📸 **Photo Information**\n\n"
|
||||||
|
else:
|
||||||
|
response_text = f"🚗 **{year} {make} {model}**\n\n"
|
||||||
|
response_text += f"📸 **Photo Information**\n\n"
|
||||||
|
|
||||||
|
response_text += f"🖼️ **{photo_count} damage photos** found in our database for this vehicle.\n"
|
||||||
|
response_text += f"These photos show the actual condition and damage of the vehicle during auction."
|
||||||
|
|
||||||
|
builder.button(text=f"Pay {IMG_PRICE} ⭐️ for photos", callback_data=f"pay_photos:{vin}", pay=True)
|
||||||
|
builder.button(text="Try another VIN", callback_data="search_car_photo")
|
||||||
|
builder.button(text="Back to Main Menu", callback_data="main_menu")
|
||||||
|
builder.adjust(1, 1, 1) # Each button on separate row
|
||||||
|
else:
|
||||||
|
# Нет фотографий
|
||||||
|
if make == "UNKNOWN" and model == "UNKNOWN" and year == "UNKNOWN":
|
||||||
|
response_text = f"❌ **Unable to decode VIN or no photos found**\n\n"
|
||||||
|
else:
|
||||||
|
response_text = f"🚗 **{year} {make} {model}**\n\n"
|
||||||
|
|
||||||
|
response_text += f"📸 **No damage photos found** for this VIN in our database."
|
||||||
|
|
||||||
|
builder.button(text="Try another VIN", callback_data="search_car_photo")
|
||||||
|
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 photo search 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).")
|
||||||
|
|
||||||
|
|
||||||
async def on_startup():
|
async def on_startup():
|
||||||
log_system_info() # Логируем информацию о системе
|
log_system_info() # Логируем информацию о системе
|
||||||
await oracle_db.connect()
|
await oracle_db.connect()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user