540 lines
20 KiB
Markdown
540 lines
20 KiB
Markdown
# 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 и автоматические пути
|
||
|
||
---
|
||
|
||
*Документация актуальна на момент последнего обновления кода.* |