From fe0aaefbec8acb7ad4ced38aa484518c6e6dd8c1 Mon Sep 17 00:00:00 2001 From: Vlad Date: Mon, 2 Jun 2025 00:48:40 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=87?= =?UTF-8?q?=D0=B8=D0=BA=D0=B8=20=D0=B2=20main.py=20=D0=B4=D0=BB=D1=8F=20?= =?UTF-8?q?=D1=83=D0=BB=D1=83=D1=87=D1=88=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BB?= =?UTF-8?q?=D0=BE=D0=B3=D0=B8=D0=BA=D0=B8=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D0=BA=D0=B8=20=D0=B7=D0=B0=D0=BF=D1=80=D0=BE=D1=81?= =?UTF-8?q?=D0=BE=D0=B2=20=D0=BD=D0=B0=20=D0=BF=D0=BE=D0=BB=D1=83=D1=87?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B8=D0=BD=D1=84=D0=BE=D1=80=D0=BC?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D0=B8=20=D0=BE=20=D0=BF=D0=BE=D0=B2=D1=80?= =?UTF-8?q?=D0=B5=D0=B6=D0=B4=D0=B5=D0=BD=D0=B8=D1=8F=D1=85.=20=D0=94?= =?UTF-8?q?=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2=D1=8B=D0=B5=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B8?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20?= =?UTF-8?q?=D1=81=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D0=BC=D0=B8=20=D0=BE=20?= =?UTF-8?q?=D0=BF=D0=BE=D0=B2=D1=80=D0=B5=D0=B6=D0=B4=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=D1=85,=20=D0=B2=D0=BA=D0=BB=D1=8E=D1=87=D0=B0=D1=8F=20?= =?UTF-8?q?=D0=BE=D0=BF=D1=82=D0=B8=D0=BC=D0=B8=D0=B7=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D1=8E=20SQL-=D0=B7=D0=B0=D0=BF=D1=80=D0=BE=D1=81=D0=BE=D0=B2?= =?UTF-8?q?=20=D0=B8=20=D1=83=D0=BB=D1=83=D1=87=D1=88=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D1=84=D0=BE=D1=80=D0=BC=D0=B0=D1=82=D0=B8=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=B2=D1=8B=D0=B2=D0=BE=D0=B4?= =?UTF-8?q?=D0=B8=D0=BC=D0=BE=D0=B9=20=D0=B8=D0=BD=D1=84=D0=BE=D1=80=D0=BC?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D0=B8.=20=D0=AD=D1=82=D0=B8=20=D0=B8=D0=B7?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BF=D0=BE=D0=B2?= =?UTF-8?q?=D1=8B=D1=88=D0=B0=D1=8E=D1=82=20=D1=82=D0=BE=D1=87=D0=BD=D0=BE?= =?UTF-8?q?=D1=81=D1=82=D1=8C=20=D0=B8=20=D1=83=D0=B4=D0=BE=D0=B1=D1=81?= =?UTF-8?q?=D1=82=D0=B2=D0=BE=20=D0=B8=D1=81=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=B1=D0=BE=D1=82=D0=B0?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- read_dev.md | 540 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 540 insertions(+) create mode 100644 read_dev.md diff --git a/read_dev.md b/read_dev.md new file mode 100644 index 0000000..54cc8c8 --- /dev/null +++ b/read_dev.md @@ -0,0 +1,540 @@ +# SalvageDB Telegram Bot - Developer Documentation + +## Оглавление +1. [Архитектура бота](#архитектура-бота) +2. [Переменные окружения](#переменные-окружения) +3. [Состояния FSM](#состояния-fsm) +4. [Основные функции](#основные-функции) +5. [Callback Handlers](#callback-handlers) +6. [Message Handlers](#message-handlers) +7. [Функции базы данных](#функции-базы-данных) +8. [Система платежей](#система-платежей) +9. [Работа с фотографиями](#работа-с-фотографиями) +10. [Системные функции](#системные-функции) +11. [Middleware](#middleware) +12. [Структура проекта](#структура-проекта) +13. [API Endpoints](#api-endpoints) + +--- + +## Архитектура бота + +Бот построен на фреймворке `aiogram 3.x` с использованием Oracle Database для хранения данных о транспортных средствах и их истории повреждений. + +### Основные компоненты: +- **main.py** - основной файл с логикой бота +- **db.py** - класс для работы с Oracle Database +- **middlewares/db.py** - middleware для передачи подключения к БД + +### Схема работы: +``` +User Input → FSM States → Database Query → Response Formatting → Telegram API +``` + +--- + +## Переменные окружения + +### Обязательные переменные: +```env +BOT_TOKEN=ваш_токен_бота +BOT_NAME=имя_бота +db_user=пользователь_oracle +db_password=пароль_oracle +db_dsn=строка_подключения_oracle +ADMIN_USER_ID=id_администратора +``` + +### Опциональные переменные: +```env +DEBUG=1 # Включает детальное логирование +DECODE_PRICE=1 # Цена за детальную информацию VIN (звезды) +CHECK_PRICE=10 # Цена за проверку salvage записей (звезды) +IMG_PRICE=100 # Цена за фотографии (звезды) +``` + +### Автоматические пути к изображениям: +```python +if is_windows(): + image_path = "D:\\SALVAGEDB\\salvagedb_bot\\images" +else: + image_path = "/images/" +``` + +--- + +## Состояния FSM + +Бот использует Finite State Machine для управления диалогами: + +```python +class VinStates(StatesGroup): + waiting_for_vin = State() # Ожидание VIN для декодирования + waiting_for_check_vin = State() # Ожидание VIN для проверки salvage + waiting_for_photo_vin = State() # Ожидание VIN для поиска фотографий +``` + +### Переходы между состояниями: +- **Start** → `waiting_for_vin` (кнопка "Decode VIN") +- **Start** → `waiting_for_check_vin` (кнопка "Check VIN") +- **Start** → `waiting_for_photo_vin` (кнопка "Search car Photo") +- **Any State** → **Start** (кнопка "Back to Main Menu") + +--- + +## Основные функции + +### Системные функции + +#### `get_operating_system() -> str` +Определяет операционную систему. +```python +Returns: 'Windows', 'Linux', 'macOS' или 'Unknown' +``` + +#### `is_windows() -> bool`, `is_linux() -> bool`, `is_macos() -> bool` +Быстрые проверки операционной системы. + +#### `log_system_info()` +Логирует подробную информацию о системе при запуске. + +### Функции обработки данных + +#### `get_us_state_name(state_code: str) -> str` +Конвертирует двухбуквенный код штата США в полное название. +```python +get_us_state_name("TX") # Returns: "Texas" +``` + +#### `format_sale_date(date_str: str) -> str` +Форматирует дату из MM/YYYY в читаемый формат. +```python +format_sale_date("3/2023") # Returns: "March 2023" +``` + +#### `parse_location(location_str: str) -> str` +Парсит локацию из формата ST/TOWN в "City, State". +```python +parse_location("TX/DALLAS") # Returns: "Dallas, Texas" +``` + +#### `escape_markdown(text: str) -> str` +Экранирует специальные символы Markdown для безопасной отправки. + +### Функции работы с фотографиями + +#### `convert_photo_path(db_path: str) -> str` +Конвертирует путь из БД в полный путь с учетом OS. +```python +# Windows +convert_photo_path("20250530/vin/photo.jpg") +# Returns: "D:\SALVAGEDB\salvagedb_bot\images\20250530\vin\photo.jpg" + +# Linux +convert_photo_path("20250530/vin/photo.jpg") +# Returns: "/images/20250530/vin/photo.jpg" +``` + +#### `prepare_photo_paths(db_paths: list) -> list` +Подготавливает список полных путей к фотографиям. + +#### `send_vehicle_photos(message, vin, photo_paths, make, model, year)` +Отправляет фотографии пользователю группами по 10 штук. + +**Особенности:** +- Разбивает фотографии на группы по 10 (лимит Telegram) +- Добавляет описания к первым фото каждой группы +- Пауза 0.5 сек между группами +- Обработка ошибок для каждой фотографии +- Итоговое сообщение с количеством отправленных фото + +--- + +## Callback Handlers + +### `@dp.callback_query(lambda c: c.data == "decode_vin")` +**Функция:** `decode_vin_callback` +**Действие:** Устанавливает состояние `waiting_for_vin`, запрашивает VIN для декодирования. + +### `@dp.callback_query(lambda c: c.data == "check_vin")` +**Функция:** `check_vin_callback` +**Действие:** Устанавливает состояние `waiting_for_check_vin`, запрашивает VIN для проверки salvage. + +### `@dp.callback_query(lambda c: c.data == "search_car_photo")` +**Функция:** `search_car_photo_callback` +**Действие:** Устанавливает состояние `waiting_for_photo_vin`, запрашивает VIN для поиска фотографий. + +### `@dp.callback_query(lambda c: c.data == "main_menu")` +**Функция:** `main_menu_callback` +**Действие:** Очищает состояние, возвращает в главное меню. + +### Платежные handlers + +#### `@dp.callback_query(lambda c: c.data.startswith("pay_detailed_info:"))` +**Функция:** `pay_detailed_info_callback` +**Payload:** `detailed_vin_info:{vin}` +**Цена:** `DECODE_PRICE` звезд + +#### `@dp.callback_query(lambda c: c.data.startswith("pay_check_detailed:"))` +**Функция:** `pay_check_detailed_callback` +**Payload:** `detailed_salvage_check:{vin}` +**Цена:** `CHECK_PRICE` звезд + +#### `@dp.callback_query(lambda c: c.data.startswith("pay_photos:"))` +**Функция:** `pay_photos_callback` +**Payload:** `vehicle_photos:{vin}` +**Цена:** `IMG_PRICE` звезд + +--- + +## Message Handlers + +### `@dp.message(Command("start"))` +**Функция:** `command_start_handler` +**Действие:** Показывает приветственное сообщение и главное меню. + +**Кнопки:** +- Decode VIN +- Check VIN +- Search car Photo +- Help +- Prices +- Go Salvagedb.com + +### `@dp.message(Command("admin_stats"))` +**Функция:** `admin_stats_handler` +**Доступ:** Только для `ADMIN_USER_ID` +**Действие:** Показывает статистику пользователей бота. + +### Обработчики состояний + +#### `@dp.message(VinStates.waiting_for_vin)` +**Функция:** `process_vin` +**Логика:** +1. Валидация VIN (17 символов, без I/O/Q) +2. Запрос `fetch_vin_info(vin)` +3. Проверка успешности декодирования (не все UNKNOWN) +4. Показ кнопки детальной информации при `cnt > 9` + +#### `@dp.message(VinStates.waiting_for_check_vin)` +**Функция:** `process_check_vin` +**Логика:** +1. Валидация VIN +2. Запрос `fetch_vin_info(vin)` и `count_salvage_records(vin)` +3. Показ информации о количестве найденных записей +4. Кнопка оплаты при наличии записей + +#### `@dp.message(VinStates.waiting_for_photo_vin)` +**Функция:** `process_photo_vin` +**Логика:** +1. Валидация VIN +2. Запрос `fetch_vin_info(vin)` и `count_photo_records(vin)` +3. Показ информации о количестве фотографий +4. Кнопка оплаты при наличии фотографий + +### `@dp.message(lambda message: message.successful_payment)` +**Функция:** `successful_payment_handler` +**Логика обработки платежей по payload:** + +#### `detailed_vin_info:{vin}` +1. Запрос `fetch_detailed_vin_info(vin)` +2. Форматирование детального отчета по категориям +3. Возврат денег при отсутствии данных +4. Автоматический возврат админу + +#### `detailed_salvage_check:{vin}` +1. Запрос `fetch_salvage_detailed_info(vin)` +2. Форматирование salvage отчета с повреждениями +3. Отдельное сообщение о фотографиях +4. Возврат денег при отсутствии данных +5. Автоматический возврат админу + +#### `vehicle_photos:{vin}` +1. Запрос `fetch_photo_paths(vin)` +2. Конвертация путей под текущую OS +3. Отправка фотографий группами по 10 +4. Возврат денег при отсутствии фотографий +5. Автоматический возврат админу + +--- + +## Функции базы данных + +### Класс `OracleDatabase` + +#### `__init__(user, password, dsn)` +Инициализация подключения к Oracle DB. + +#### `async def connect()` +Создает пул подключений (min=1, max=4). + +#### `async def fetch_vin_info(vin: str) -> Tuple[str, str, str, int]` +**Возвращает:** `(make, model, year, nhtsa_records_count)` +**SQL запрос:** +```sql +select 'None', + COALESCE((select value from salvagedb.m_JSONS_FROM_NHTSA v3 + where v3.svin = s.svin and v3.variableid = '26'), + (select val from salvagedb.vind2 + where svin = substr(s.vin, 1, 8) || '*' || substr(s.vin, 10, 2) + and varb = 'Make'),'UNKNOWN') make, + -- аналогично для model и year + (select count(*) from salvagedb.m_JSONS_FROM_NHTSA v3 + where v3.svin = s.svin) cnt +from (select substr(:vin,1,10) svin, :vin vin from dual) s +``` + +#### `async def count_salvage_records(vin: str) -> int` +**SQL:** `SELECT COUNT(*) FROM salvagedb.salvagedb WHERE vin = :vin and svin = substr(:vin,1,10)` + +#### `async def count_photo_records(vin: str) -> int` +**SQL:** `SELECT COUNT(*) FROM salvagedb.salvage_images WHERE vin = :vin AND fn = 1` + +#### `async def fetch_photo_paths(vin: str) -> list` +**SQL:** `SELECT ipath FROM salvagedb.salvage_images WHERE fn = 1 AND vin = :vin` +**Возвращает:** Список путей к фотографиям. + +#### `async def fetch_salvage_detailed_info(vin: str) -> list` +**Возвращает:** Детальную информацию о salvage записях. +**SQL запрос:** +```sql +SELECT + odo, odos, dem1, dem2, + month||'/'||year as sale_date, + JSON_VALUE(jdata, '$.RepCost') AS j_rep_cost, + JSON_VALUE(jdata, '$.Runs_Drive') AS j_runs_drive, + JSON_VALUE(jdata, '$.Locate') AS j_locate, + (select count(*) from salvagedb.salvage_images si + where si.vin = s.vin and fn = 1) img_count +FROM salvagedb.salvagedb s +LEFT JOIN salvagedb.addinfo i ON s.num = i.numid +WHERE vin = :vin AND svin = substr(:vin, 1, 10) +ORDER BY year DESC, month DESC +``` + +#### `async def fetch_detailed_vin_info(vin: str) -> dict` +**Возвращает:** Детальную информацию о VIN по категориям. +**Категории:** +- basic_characteristics +- engine_and_powertrain +- transmission +- active_safety +- passive_safety +- dimensions_and_construction +- brake_system +- lighting +- additional_features +- manufacturing_and_localization +- ncsa_data +- technical_information_and_errors + +#### Пользовательские функции + +#### `async def save_user(user: User, interaction_source: str) -> bool` +Сохраняет/обновляет данные пользователя при каждом взаимодействии. + +#### `async def update_user_payment(user_id: int, payment_amount: float) -> bool` +Обновляет статистику платежей пользователя. + +#### `async def get_user_stats(user_id: int) -> dict` +Возвращает статистику конкретного пользователя. + +#### `async def get_users_summary() -> dict` +Возвращает общую статистику по всем пользователям. + +--- + +## Система платежей + +### Telegram Stars +Бот использует Telegram Stars для приема платежей. + +### Типы платежей: +1. **Детальная информация VIN** - `DECODE_PRICE` звезд +2. **Salvage проверка** - `CHECK_PRICE` звезд +3. **Фотографии автомобиля** - `IMG_PRICE` звезд + +### Процесс оплаты: +1. Пользователь нажимает кнопку оплаты +2. Создается invoice с уникальным payload +3. Pre-checkout подтверждение +4. Successful payment обработка +5. Предоставление услуги или возврат средств + +### Автоматический возврат админу: +Если `message.from_user.id == ADMIN_USER_ID`, после предоставления услуги автоматически возвращаются потраченные звезды. + +### Обработка ошибок: +- Отсутствие данных → автоматический возврат +- Ошибка сервера → автоматический возврат +- Ошибка возврата → уведомление с transaction ID + +--- + +## Работа с фотографиями + +### Структура хранения: +``` +База данных: 20250530/1c4hjxdg0pw697757/photo.jpg +Windows: D:\SALVAGEDB\salvagedb_bot\images\20250530\1c4hjxdg0pw697757\photo.jpg +Linux: /images/20250530/1c4hjxdg0pw697757/photo.jpg +``` + +### Процесс отправки: +1. Получение путей из БД +2. Конвертация под текущую OS +3. Разбивка на группы по 10 фотографий +4. Отправка media groups с описаниями +5. Итоговое сообщение + +### Ограничения: +- Максимум 10 фотографий в одной группе (Telegram API) +- Пауза 0.5 сек между группами +- Проверка существования файлов + +--- + +## Системные функции + +### Логирование: +```python +# Основное логирование +logging.basicConfig(level=logging.WARNING) + +# Debug режим +if getenv("DEBUG",'0') == '1': + logging.basicConfig(level=logging.INFO) +``` + +### Middleware: +```python +# Передача подключения к БД во все handlers +dp.message.middleware(DbSessionMiddleware(oracle_db)) +dp.callback_query.middleware(DbSessionMiddleware(oracle_db)) +dp.pre_checkout_query.middleware(DbSessionMiddleware(oracle_db)) +``` + +### Startup/Shutdown: +```python +async def on_startup(): + log_system_info() # Системная информация + await oracle_db.connect() # Подключение к БД + # Регистрация middleware + +async def on_shutdown(): + await oracle_db.close() # Закрытие пула подключений +``` + +--- + +## Middleware + +### `DbSessionMiddleware` +**Файл:** `middlewares/db.py` +**Назначение:** Передает экземпляр подключения к БД во все handlers. + +**Использование:** +```python +async def handler(message: Message, db: OracleDatabase = None): + database = db or oracle_db # Fallback на глобальный +``` + +--- + +## Структура проекта + +``` +salvagedb_bot/ +├── main.py # Основной файл бота +├── db.py # Класс работы с БД +├── read_dev.md # Документация (этот файл) +├── middlewares/ +│ └── db.py # Middleware для БД +└── images/ # Директория с фотографиями (Windows) + └── 20250530/ + └── vin_lower/ + └── photo.jpg +``` + +--- + +## API Endpoints + +### Telegram Bot API +Бот использует стандартные методы Telegram Bot API: + +#### Отправка сообщений: +- `message.answer()` - текстовые сообщения +- `message.answer_media_group()` - группы фотографий + +#### Платежи: +- `bot.send_invoice()` - создание счета +- `bot.refund_star_payment()` - возврат звезд + +#### Webhook vs Polling: +Текущая конфигурация использует polling: +```python +await dp.start_polling(bot) +``` + +--- + +## Примеры использования + +### Запуск бота: +```bash +# Установка переменных окружения +export BOT_TOKEN="your_token" +export db_user="oracle_user" +# ... другие переменные + +# Запуск +python main.py +``` + +### Тестирование платежей: +1. Установить `ADMIN_USER_ID` на свой Telegram ID +2. Выполнить любую оплату +3. Получить услугу + автоматический возврат звезд + +### Debug режим: +```bash +export DEBUG=1 +python main.py +``` +Включает детальное логирование всех операций. + +--- + +## Troubleshooting + +### Проблемы с фотографиями: +1. Проверить права доступа к директории images +2. Убедиться в правильности путей для текущей OS +3. Проверить логи на ошибки файловой системы + +### Проблемы с БД: +1. Проверить строку подключения DSN +2. Убедиться в доступности Oracle DB +3. Проверить права пользователя БД + +### Проблемы с платежами: +1. Убедиться в корректности BOT_TOKEN +2. Проверить что бот добавлен в Telegram Stars +3. Проверить логи на ошибки Telegram API + +--- + +## Обновления и изменения + +При внесении изменений в код обязательно обновляйте эту документацию. + +### История изменений: +- **v1.0** - Базовая функциональность (decode VIN, check VIN) +- **v1.1** - Добавлена поддержка фотографий +- **v1.2** - Улучшена отправка множественных фотографий +- **v1.3** - Добавлены функции определения OS и автоматические пути + +--- + +*Документация актуальна на момент последнего обновления кода.* \ No newline at end of file