mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-01-20 03:40:26 +00:00
update
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -12,6 +12,7 @@ docker-compose.override.yml
|
||||
!requirements.txt
|
||||
!docs/
|
||||
!docs/**
|
||||
!QWEN.md
|
||||
|
||||
# Разрешаем папку app/ и все её содержимое рекурсивно
|
||||
!app/
|
||||
|
||||
255
QWEN.md
Normal file
255
QWEN.md
Normal file
@@ -0,0 +1,255 @@
|
||||
# Qwen Code Context - Remnawave Bedolaga Telegram Bot
|
||||
|
||||
## Project Overview
|
||||
|
||||
The Remnawave Bedolaga Telegram Bot is a modern VPN subscription management system built with Python, aiogram, and FastAPI. It provides a comprehensive solution for managing VPN subscriptions through the Remnawave API with extensive features including multi-channel payments, user management, admin functionality, and automated systems.
|
||||
|
||||
## Architecture and Technology Stack
|
||||
|
||||
### Core Technologies
|
||||
- **Python 3.13+** with AsyncIO for maximum performance
|
||||
- **aiogram 3** for Telegram Bot API interactions
|
||||
- **FastAPI** for REST API services
|
||||
- **PostgreSQL 15+** for reliable data storage (with SQLite fallback)
|
||||
- **Redis** for caching and session management
|
||||
- **SQLAlchemy ORM** for safe database operations
|
||||
- **Pydantic v2** for data validation
|
||||
|
||||
### Key Components
|
||||
- **Payment Service**: Supports multiple payment methods (Telegram Stars, YooKassa, Tribute, CryptoBot, Heleket, MulenPay, Pal24, WATA)
|
||||
- **Subscription Service**: Manages VPN subscriptions and syncs with Remnawave panel
|
||||
- **Monitoring Service**: Regular API checks and automatic maintenance mode
|
||||
- **Backup Service**: Smart automatic backups with scheduling
|
||||
- **Web API**: Full-featured admin API with endpoints for all aspects
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
remnawave-bedolaga-telegram-bot/
|
||||
├── app/ # Main application code
|
||||
│ ├── bot.py # Bot initialization and setup
|
||||
│ ├── config.py # Configuration and settings
|
||||
│ ├── database/ # Database models and CRUD operations
|
||||
│ ├── external/ # External webhook servers
|
||||
│ ├── handlers/ # Bot command and callback handlers
|
||||
│ ├── keyboards/ # Inline and reply keyboards
|
||||
│ ├── localization/ # Translation and locale management
|
||||
│ ├── middlewares/ # Bot middlewares
|
||||
│ ├── services/ # Business logic services
|
||||
│ ├── utils/ # Utility functions
|
||||
│ ├── webapi/ # Web API endpoints
|
||||
│ └── states.py # FSM states
|
||||
├── assets/ # Project assets
|
||||
├── data/ # Runtime data storage
|
||||
├── docs/ # Documentation
|
||||
├── logs/ # Log files
|
||||
├── .env.example # Environment configuration template
|
||||
├── docker-compose.yml # Docker services configuration
|
||||
├── Dockerfile # Docker image definition
|
||||
├── install_bot.sh # Installation and management script
|
||||
├── main.py # Main application entry point
|
||||
├── README.md # Project documentation
|
||||
└── requirements.txt # Python dependencies
|
||||
```
|
||||
|
||||
## Key Features
|
||||
|
||||
### For Users
|
||||
- **Multi-channel payments**: Telegram Stars, Tribute, CryptoBot, YooKassa, MulenPay, PayPalych, WATA
|
||||
- **Smart subscription purchase**: Flexible periods with discounts, traffic selection, server selection
|
||||
- **Trial subscriptions**: Configurable trial with welcome sequence
|
||||
- **Auto-pay**: Configurable auto-renewal with day selection
|
||||
- **Referral system**: Commission-based referral program
|
||||
- **MiniApp dashboard**: Full personal account inside Telegram
|
||||
- **Multiple languages**: RU/EN with dynamic localization
|
||||
|
||||
### For Administrators
|
||||
- **Analytics**: Detailed dashboards for users, subscriptions, and payments
|
||||
- **User management**: Search, filters, detailed user cards
|
||||
- **Promo system**: Codes, groups, personal offers
|
||||
- **Ticket system**: Support with priorities and SLA
|
||||
- **Backup and restore**: Smart automatic backups with scheduling
|
||||
- **Web API**: Full integration capabilities
|
||||
- **Monitoring**: Server monitoring and health checks
|
||||
|
||||
## Configuration
|
||||
|
||||
### Environment Variables
|
||||
The bot is configured through `.env` file with comprehensive settings including:
|
||||
- Bot token and admin IDs
|
||||
- Remnawave panel integration (URL, API key, secret key)
|
||||
- Database configuration (PostgreSQL or SQLite)
|
||||
- Payment system configurations
|
||||
- Pricing and subscription settings
|
||||
- Feature flags and UI options
|
||||
|
||||
### Key Configuration Options
|
||||
- `BOT_TOKEN`: Telegram bot token from BotFather
|
||||
- `REMNAWAVE_API_URL`: Your Remnawave panel URL
|
||||
- `REMNAWAVE_API_KEY`: API key for your panel
|
||||
- `REMNAWAVE_SECRET_KEY`: Panel protection key (for eGames panels)
|
||||
- `ADMIN_IDS`: Telegram IDs of administrators
|
||||
- Payment system specific configurations
|
||||
- Database and Redis settings
|
||||
|
||||
## Building and Running
|
||||
|
||||
### Docker Setup (Recommended)
|
||||
The project uses Docker Compose for easy deployment:
|
||||
|
||||
```bash
|
||||
# Clone repository
|
||||
git clone https://github.com/Fr1ngg/remnawave-bedolaga-telegram-bot.git
|
||||
cd remnawave-bedolaga-telegram-bot
|
||||
|
||||
# Configure environment
|
||||
cp .env.example .env
|
||||
# Edit .env with your settings
|
||||
|
||||
# Create necessary directories
|
||||
mkdir -p ./logs ./data ./data/backups ./data/referral_qr
|
||||
chmod -R 755 ./logs ./data
|
||||
sudo chown -R 1000:1000 ./logs ./data
|
||||
|
||||
# Start all services
|
||||
docker compose up -d
|
||||
|
||||
# Check status
|
||||
docker compose logs
|
||||
```
|
||||
|
||||
### Management Script
|
||||
The `install_bot.sh` script provides an interactive management menu:
|
||||
- Container status monitoring and resource monitoring
|
||||
- Service management (start/stop/rebuild)
|
||||
- Log viewing and search
|
||||
- Git update with automatic backup
|
||||
- Backup and restore functionality
|
||||
- Environment configuration
|
||||
|
||||
## Key Services and Components
|
||||
|
||||
### Payment Service
|
||||
Handles all payment system integrations with support for:
|
||||
- Telegram Stars (built-in)
|
||||
- YooKassa (SBP and cards)
|
||||
- Tribute (cryptocurrency)
|
||||
- CryptoBot (USDT, TON, BTC, ETH and others)
|
||||
- Heleket (cryptocurrency with markup)
|
||||
- MulenPay (SBP)
|
||||
- PayPalych/Pal24 (SBP and cards)
|
||||
- WATA (bank card payments)
|
||||
|
||||
### Subscription Service
|
||||
Manages VPN subscriptions and integrates with Remnawave API:
|
||||
- Creates and updates users in the Remnawave panel
|
||||
- Handles subscription expiration and traffic limits
|
||||
- Supports different traffic reset strategies
|
||||
- Syncs subscription usage and status
|
||||
|
||||
### Web API and MiniApp
|
||||
- FastAPI Web API with endpoints for all aspects
|
||||
- MiniApp personal account inside Telegram
|
||||
- Integrated payments in MiniApp
|
||||
- App Config for centralized link distribution
|
||||
|
||||
### Database and Storage
|
||||
- PostgreSQL: Primary database for user data and subscriptions
|
||||
- Redis: Fast caching and session storage for cart functionality
|
||||
- Migration system: Automatic schema updates
|
||||
- Backup system: Automatic database backups
|
||||
|
||||
## Development Conventions
|
||||
|
||||
### Code Structure
|
||||
- **Modular architecture**: Subscription and payment modules are separate
|
||||
- **AsyncIO**: All operations are asynchronous for maximum performance
|
||||
- **Type hints**: Full type annotation coverage
|
||||
- **Dependency injection**: Services are properly injected and managed
|
||||
- **Pydantic models**: Data validation and configuration management
|
||||
|
||||
### Error Handling
|
||||
- **Graceful shutdown**: Proper cleanup on termination signals
|
||||
- **Service restart**: Automatic restart of failed services
|
||||
- **Comprehensive logging**: Detailed logs for debugging and monitoring
|
||||
- **Middleware protection**: Global error handling for callback queries
|
||||
|
||||
### Testing and Quality
|
||||
- The codebase includes various test files and debugging tools
|
||||
- Uses pytest for testing (evidenced by .pytest_cache directory)
|
||||
- Type checking supported through development practices
|
||||
|
||||
## Security Features
|
||||
|
||||
- **API key validation**: All external API calls validated
|
||||
- **Rate limiting**: Protection against spam
|
||||
- **Data encryption**: Sensitive data encrypted
|
||||
- **Session management**: Automatic session management
|
||||
- **Suspicious activity monitoring**: Activity monitoring and alerts
|
||||
- **Username blocking**: Automatic blocking of suspicious names
|
||||
|
||||
## Health Checks and Monitoring
|
||||
|
||||
- Main: `http://localhost:8081/health`
|
||||
- YooKassa: `http://localhost:8082/health`
|
||||
- Pal24: `http://localhost:8084/health`
|
||||
|
||||
## Troubleshooting Commands
|
||||
|
||||
```bash
|
||||
# View logs in real-time
|
||||
docker compose logs -f bot
|
||||
|
||||
# Status of all containers
|
||||
docker compose ps
|
||||
|
||||
# Restart only the bot
|
||||
docker compose restart bot
|
||||
|
||||
# Check database
|
||||
docker compose exec postgres pg_isready -U remnawave_user
|
||||
|
||||
# Connect to database
|
||||
docker compose exec postgres psql -U remnawave_user -d remnawave_bot
|
||||
|
||||
# Check Redis
|
||||
docker compose exec redis redis-cli ping
|
||||
```
|
||||
|
||||
## Main Entry Point
|
||||
|
||||
The application starts in `main.py` which:
|
||||
- Initializes the database and runs migrations
|
||||
- Sets up the bot with aiogram
|
||||
- Starts webhook servers for various payment systems
|
||||
- Launches background services (monitoring, maintenance, version checking)
|
||||
- Begins polling for Telegram updates
|
||||
- Handles graceful shutdown
|
||||
|
||||
## Localization
|
||||
|
||||
The bot supports multiple languages (RU/EN) with:
|
||||
- Dynamic language selection
|
||||
- Comprehensive translation files
|
||||
- Localized user interface elements
|
||||
- Proper formatting for prices and dates
|
||||
|
||||
## Payment Integration
|
||||
|
||||
The bot supports multiple payment systems with:
|
||||
- Webhook handling for payment confirmations
|
||||
- Automatic balance updates
|
||||
- Transaction history tracking
|
||||
- Refund and cancellation support
|
||||
- Payment method-specific configurations
|
||||
|
||||
## Administrative Features
|
||||
|
||||
Administrative functionality includes:
|
||||
- User management (search, edit, ban)
|
||||
- Subscription management (create, modify, extend)
|
||||
- Promotional tools (codes, groups, offers)
|
||||
- Payment configuration
|
||||
- System monitoring and health checks
|
||||
- Backup and restore capabilities
|
||||
- Analytics and reporting
|
||||
@@ -395,6 +395,8 @@ async def handle_successful_topup_with_cart(
|
||||
success_text = (
|
||||
f"✅ Баланс пополнен на {texts.format_price(amount_kopeks)}!\n\n"
|
||||
f"💰 Текущий баланс: {texts.format_price(user.balance_kopeks)}\n\n"
|
||||
f"⚠️ <b>Важно:</b> Пополнение баланса не активирует подписку автоматически. "
|
||||
f"Обязательно активируйте подписку отдельно!\n\n"
|
||||
f"🛒 У вас есть сохраненная корзина подписки\n"
|
||||
f"Стоимость: {texts.format_price(total_price)}\n\n"
|
||||
f"Хотите продолжить оформление?"
|
||||
|
||||
@@ -130,6 +130,8 @@ async def handle_successful_payment(
|
||||
"⭐ Потрачено звезд: {stars_spent}\n"
|
||||
"💰 Зачислено на баланс: {amount} ₽\n"
|
||||
"🆔 ID транзакции: {transaction_id}...\n\n"
|
||||
"⚠️ <b>Важно:</b> Пополнение баланса не активирует подписку автоматически. "
|
||||
"Обязательно активируйте подписку отдельно!\n\n"
|
||||
"Спасибо за пополнение! 🚀",
|
||||
).format(
|
||||
stars_spent=payment.total_amount,
|
||||
|
||||
@@ -114,7 +114,9 @@ async def handle_successful_payment(message: types.Message):
|
||||
)
|
||||
|
||||
await message.answer(
|
||||
f"✅ Баланс успешно пополнен на {settings.format_price(amount_kopeks)}!"
|
||||
f"✅ Баланс успешно пополнен на {settings.format_price(amount_kopeks)}!\n\n"
|
||||
"⚠️ <b>Важно:</b> Пополнение баланса не активирует подписку автоматически. "
|
||||
"Обязательно активируйте подписку отдельно!"
|
||||
)
|
||||
|
||||
logger.info(f"✅ Обработан Stars платеж: {payment.telegram_payment_charge_id}")
|
||||
|
||||
@@ -1242,7 +1242,7 @@
|
||||
"SKIP_BUTTON": "Skip ➡️",
|
||||
"STARS_PAYMENT_ENROLLMENT_ERROR": "❌ Failed to credit funds. Please contact support; the payment will be verified manually.",
|
||||
"STARS_PAYMENT_PROCESSING_ERROR": "❌ Technical error processing the payment. Please contact support for assistance.",
|
||||
"STARS_PAYMENT_SUCCESS": "🎉 <b>Payment processed successfully!</b>\n\n⭐ Stars spent: {stars_spent}\n💰 Added to balance: {amount} ₽\n🆔 Transaction ID: {transaction_id}...\n\nThank you for topping up! 🚀",
|
||||
"STARS_PAYMENT_SUCCESS": "🎉 <b>Payment processed successfully!</b>\n\n⭐ Stars spent: {stars_spent}\n💰 Added to balance: {amount} ₽\n🆔 Transaction ID: {transaction_id}...\n\n⚠️ <b>Important:</b> Balance top-up does not automatically activate your subscription. Please activate your subscription separately!\n\nThank you for topping up! 🚀",
|
||||
"STARS_PAYMENT_USER_NOT_FOUND": "❌ Error: user not found. Please contact support.",
|
||||
"STARS_PRECHECK_INVALID_PAYLOAD": "Payment validation error. Please try again.",
|
||||
"STARS_PRECHECK_TECHNICAL_ERROR": "Technical error. Please try again later.",
|
||||
|
||||
@@ -1262,7 +1262,7 @@
|
||||
"SKIP_BUTTON": "⏭️ Пропустить",
|
||||
"STARS_PAYMENT_ENROLLMENT_ERROR": "❌ Произошла ошибка при зачислении средств. Обратитесь в поддержку, платеж будет проверен вручную.",
|
||||
"STARS_PAYMENT_PROCESSING_ERROR": "❌ Техническая ошибка при обработке платежа. Обратитесь в поддержку для решения проблемы.",
|
||||
"STARS_PAYMENT_SUCCESS": "🎉 <b>Платеж успешно обработан!</b>\n\n⭐ Потрачено звезд: {stars_spent}\n💰 Зачислено на баланс: {amount} ₽\n🆔 ID транзакции: {transaction_id}...\n\nСпасибо за пополнение! 🚀",
|
||||
"STARS_PAYMENT_SUCCESS": "🎉 <b>Платеж успешно обработан!</b>\n\n⭐ Потрачено звезд: {stars_spent}\n💰 Зачислено на баланс: {amount} ₽\n🆔 ID транзакции: {transaction_id}...\n\n⚠️ <b>Важно:</b> Пополнение баланса не активирует подписку автоматически. Обязательно активируйте подписку отдельно!\n\nСпасибо за пополнение! 🚀",
|
||||
"STARS_PAYMENT_USER_NOT_FOUND": "❌ Ошибка: пользователь не найден. Обратитесь в поддержку.",
|
||||
"STARS_PRECHECK_INVALID_PAYLOAD": "Ошибка валидации платежа. Попробуйте еще раз.",
|
||||
"STARS_PRECHECK_TECHNICAL_ERROR": "Техническая ошибка. Попробуйте позже.",
|
||||
|
||||
@@ -42,6 +42,7 @@ class PaymentCommonMixin:
|
||||
user and user.subscription and not user.subscription.is_trial and user.subscription.is_active
|
||||
)
|
||||
|
||||
# Создаем основную кнопку: если есть активная подписка - продлить, иначе купить
|
||||
first_button = build_miniapp_or_callback_button(
|
||||
text=(
|
||||
texts.MENU_EXTEND_SUBSCRIPTION
|
||||
@@ -53,7 +54,16 @@ class PaymentCommonMixin:
|
||||
),
|
||||
)
|
||||
|
||||
keyboard_rows: list[list[InlineKeyboardButton]] = [[first_button]]
|
||||
# Кнопка активации подписки (всегда отображается)
|
||||
activate_subscription_button = build_miniapp_or_callback_button(
|
||||
text="🚀 Активировать подписку",
|
||||
callback_data="menu_buy" # Используем ту же callback_data что и "Купить подписку"
|
||||
)
|
||||
|
||||
keyboard_rows: list[list[InlineKeyboardButton]] = [
|
||||
[first_button],
|
||||
[activate_subscription_button]
|
||||
]
|
||||
|
||||
# Если для пользователя есть незавершённый checkout, предлагаем вернуться к нему.
|
||||
if user:
|
||||
@@ -128,7 +138,9 @@ class PaymentCommonMixin:
|
||||
"✅ <b>Платеж успешно завершен!</b>\n\n"
|
||||
f"💰 Сумма: {settings.format_price(amount_kopeks)}\n"
|
||||
f"💳 Способ: {payment_method}\n\n"
|
||||
"Средства зачислены на ваш баланс!"
|
||||
"Средства зачислены на ваш баланс!\n\n"
|
||||
"⚠️ <b>Важно:</b> Пополнение баланса не активирует подписку автоматически. "
|
||||
"Обязательно активируйте подписку отдельно!"
|
||||
)
|
||||
|
||||
await self.bot.send_message(
|
||||
|
||||
@@ -346,7 +346,9 @@ class CryptoBotPaymentMixin:
|
||||
|
||||
await self.bot.send_message(
|
||||
chat_id=user.telegram_id,
|
||||
text=f"✅ Баланс пополнен на {settings.format_price(payment.amount_kopeks)}!\n\n{cart_message}",
|
||||
text=f"✅ Баланс пополнен на {settings.format_price(payment.amount_kopeks)}!\n\n"
|
||||
f"⚠️ <b>Важно:</b> Пополнение баланса не активирует подписку автоматически. "
|
||||
f"Обязательно активируйте подписку отдельно!\n\n{cart_message}",
|
||||
reply_markup=keyboard
|
||||
)
|
||||
logger.info(
|
||||
|
||||
@@ -389,7 +389,9 @@ class MulenPayPaymentMixin:
|
||||
|
||||
await self.bot.send_message(
|
||||
chat_id=user.telegram_id,
|
||||
text=f"✅ Баланс пополнен на {settings.format_price(payment.amount_kopeks)}!\n\n{cart_message}",
|
||||
text=f"✅ Баланс пополнен на {settings.format_price(payment.amount_kopeks)}!\n\n"
|
||||
f"⚠️ <b>Важно:</b> Пополнение баланса не активирует подписку автоматически. "
|
||||
f"Обязательно активируйте подписку отдельно!\n\n{cart_message}",
|
||||
reply_markup=keyboard
|
||||
)
|
||||
logger.info(
|
||||
|
||||
@@ -489,7 +489,9 @@ class Pal24PaymentMixin:
|
||||
chat_id=user.telegram_id,
|
||||
text=(
|
||||
"✅ Баланс пополнен на "
|
||||
f"{settings.format_price(payment.amount_kopeks)}!\n\n{cart_message}"
|
||||
f"{settings.format_price(payment.amount_kopeks)}!\n\n"
|
||||
f"⚠️ <b>Важно:</b> Пополнение баланса не активирует подписку автоматически. "
|
||||
f"Обязательно активируйте подписку отдельно!\n\n{cart_message}"
|
||||
),
|
||||
reply_markup=keyboard,
|
||||
)
|
||||
|
||||
@@ -498,6 +498,8 @@ class PlategaPaymentMixin:
|
||||
chat_id=user.telegram_id,
|
||||
text=(
|
||||
f"✅ Баланс пополнен на {settings.format_price(payment.amount_kopeks)}!\n\n"
|
||||
f"⚠️ <b>Важно:</b> Пополнение баланса не активирует подписку автоматически. "
|
||||
f"Обязательно активируйте подписку отдельно!\n\n"
|
||||
f"{cart_message}"
|
||||
),
|
||||
reply_markup=keyboard,
|
||||
|
||||
@@ -604,7 +604,9 @@ class TelegramStarsMixin:
|
||||
|
||||
await self.bot.send_message(
|
||||
chat_id=user.telegram_id,
|
||||
text=f"✅ Баланс пополнен на {settings.format_price(amount_kopeks)}!\n\n{cart_message}",
|
||||
text=f"✅ Баланс пополнен на {settings.format_price(amount_kopeks)}!\n\n"
|
||||
f"⚠️ <b>Важно:</b> Пополнение баланса не активирует подписку автоматически. "
|
||||
f"Обязательно активируйте подписку отдельно!\n\n{cart_message}",
|
||||
reply_markup=keyboard,
|
||||
)
|
||||
logger.info(
|
||||
|
||||
@@ -514,6 +514,8 @@ class WataPaymentMixin:
|
||||
f"💰 Сумма: {settings.format_price(payment.amount_kopeks)}\n"
|
||||
"🦊 Способ: WATA\n"
|
||||
f"🆔 Транзакция: {transaction.id}\n\n"
|
||||
"⚠️ <b>Важно:</b> Пополнение баланса не активирует подписку автоматически. "
|
||||
"Обязательно активируйте подписку отдельно!\n\n"
|
||||
"Баланс пополнен автоматически!"
|
||||
),
|
||||
parse_mode="HTML",
|
||||
|
||||
@@ -554,7 +554,9 @@ class YooKassaPaymentMixin:
|
||||
|
||||
await self.bot.send_message(
|
||||
chat_id=user.telegram_id,
|
||||
text=f"✅ Баланс пополнен на {settings.format_price(payment.amount_kopeks)}!\n\n{cart_message}",
|
||||
text=f"✅ Баланс пополнен на {settings.format_price(payment.amount_kopeks)}!\n\n"
|
||||
f"⚠️ <b>Важно:</b> Пополнение баланса не активирует подписку автоматически. "
|
||||
f"Обязательно активируйте подписку отдельно!\n\n{cart_message}",
|
||||
reply_markup=keyboard,
|
||||
)
|
||||
logger.info(
|
||||
|
||||
@@ -257,6 +257,8 @@ class TributeService:
|
||||
f"💰 Сумма: {int(amount_rubles)} ₽\n"
|
||||
f"💳 Способ оплаты: Tribute\n"
|
||||
f"🎉 Средства зачислены на баланс!\n\n"
|
||||
f"⚠️ <b>Важно:</b> Пополнение баланса не активирует подписку автоматически. "
|
||||
f"Обязательно активируйте подписку отдельно!\n\n"
|
||||
f"Спасибо за оплату! 🙏"
|
||||
)
|
||||
|
||||
@@ -318,7 +320,9 @@ class TributeService:
|
||||
|
||||
await self.bot.send_message(
|
||||
chat_id=user_id,
|
||||
text=f"✅ Баланс пополнен на {settings.format_price(amount_kopeks)}!\n\n{cart_message}",
|
||||
text=f"✅ Баланс пополнен на {settings.format_price(amount_kopeks)}!\n\n"
|
||||
f"⚠️ <b>Важно:</b> Пополнение баланса не активирует подписку автоматически. "
|
||||
f"Обязательно активируйте подписку отдельно!\n\n{cart_message}",
|
||||
reply_markup=keyboard
|
||||
)
|
||||
logger.info(
|
||||
|
||||
@@ -158,6 +158,12 @@ async def get_user(
|
||||
_: Any = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> UserResponse:
|
||||
# First check if the provided ID is a telegram_id
|
||||
user = await get_user_by_telegram_id(db, user_id)
|
||||
if user:
|
||||
return _serialize_user(user)
|
||||
|
||||
# If not found as telegram_id, check as internal user ID
|
||||
user = await get_user_by_id(db, user_id)
|
||||
if not user:
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND, "User not found")
|
||||
@@ -202,8 +208,15 @@ async def update_user_endpoint(
|
||||
_: Any = Security(require_api_token),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> UserResponse:
|
||||
user = await get_user_by_id(db, user_id)
|
||||
if not user:
|
||||
# First check if the provided ID is a telegram_id
|
||||
user = await get_user_by_telegram_id(db, user_id)
|
||||
if user:
|
||||
found_user = user
|
||||
else:
|
||||
# If not found as telegram_id, check as internal user ID
|
||||
found_user = await get_user_by_id(db, user_id)
|
||||
|
||||
if not found_user:
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND, "User not found")
|
||||
|
||||
updates: dict[str, Any] = {}
|
||||
@@ -234,18 +247,23 @@ async def update_user_endpoint(
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST, "Promo group not found")
|
||||
updates["promo_group_id"] = promo_group.id
|
||||
|
||||
if payload.referral_code is not None and payload.referral_code != user.referral_code:
|
||||
if payload.referral_code is not None and payload.referral_code != found_user.referral_code:
|
||||
existing_code_owner = await get_user_by_referral_code(db, payload.referral_code)
|
||||
if existing_code_owner and existing_code_owner.id != user.id:
|
||||
if existing_code_owner and existing_code_owner.id != found_user.id:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST, "Referral code already in use")
|
||||
updates["referral_code"] = payload.referral_code
|
||||
|
||||
if not updates:
|
||||
return _serialize_user(user)
|
||||
return _serialize_user(found_user)
|
||||
|
||||
user = await update_user(db, user, **updates)
|
||||
user = await get_user_by_id(db, user.id)
|
||||
return _serialize_user(user)
|
||||
found_user = await update_user(db, found_user, **updates)
|
||||
# Reload the user to ensure we have the latest data
|
||||
if found_user.telegram_id == user_id:
|
||||
found_user = await get_user_by_telegram_id(db, user_id)
|
||||
else:
|
||||
found_user = await get_user_by_id(db, found_user.id)
|
||||
|
||||
return _serialize_user(found_user)
|
||||
|
||||
|
||||
@router.post("/{user_id}/balance", response_model=UserResponse)
|
||||
@@ -258,13 +276,20 @@ async def update_balance(
|
||||
if payload.amount_kopeks == 0:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST, "Amount must be non-zero")
|
||||
|
||||
user = await get_user_by_id(db, user_id)
|
||||
if not user:
|
||||
# First check if the provided ID is a telegram_id
|
||||
user = await get_user_by_telegram_id(db, user_id)
|
||||
if user:
|
||||
found_user = user
|
||||
else:
|
||||
# If not found as telegram_id, check as internal user ID
|
||||
found_user = await get_user_by_id(db, user_id)
|
||||
|
||||
if not found_user:
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND, "User not found")
|
||||
|
||||
success = await add_user_balance(
|
||||
db,
|
||||
user,
|
||||
found_user,
|
||||
amount_kopeks=payload.amount_kopeks,
|
||||
description=payload.description or "Корректировка через веб-API",
|
||||
create_transaction=payload.create_transaction,
|
||||
@@ -273,5 +298,10 @@ async def update_balance(
|
||||
if not success:
|
||||
raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR, "Failed to update balance")
|
||||
|
||||
user = await get_user_by_id(db, user_id)
|
||||
return _serialize_user(user)
|
||||
# Reload the user to ensure we have the latest data
|
||||
if found_user.telegram_id == user_id:
|
||||
found_user = await get_user_by_telegram_id(db, user_id)
|
||||
else:
|
||||
found_user = await get_user_by_id(db, found_user.id)
|
||||
|
||||
return _serialize_user(found_user)
|
||||
|
||||
@@ -105,10 +105,10 @@ curl -X POST "http://127.0.0.1:8080/tokens" \
|
||||
| `PUT` | `/settings/{key}` | Обновить значение настройки.
|
||||
| `DELETE` | `/settings/{key}` | Сбросить настройку к значению по умолчанию.
|
||||
| `GET` | `/users` | Список пользователей с фильтрами и пагинацией.
|
||||
| `GET` | `/users/{id}` | Детали пользователя.
|
||||
| `GET` | `/users/{id}` | Детали пользователя. ID может быть как внутренним (user.id), так и Telegram ID (user.telegram_id).
|
||||
| `POST` | `/users` | Создать пользователя (например, для ручной выдачи доступа).
|
||||
| `PATCH` | `/users/{id}` | Обновить профиль пользователя или статус.
|
||||
| `POST` | `/users/{id}/balance` | Корректировка баланса с созданием транзакции.
|
||||
| `PATCH` | `/users/{id}` | Обновить профиль пользователя или статус. ID может быть как внутренним (user.id), так и Telegram ID (user.telegram_id).
|
||||
| `POST` | `/users/{id}/balance` | Корректировка баланса с созданием транзакции. ID может быть как внутренним (user.id), так и Telegram ID (user.telegram_id).
|
||||
| `GET` | `/subscriptions` | Список подписок с фильтрами.
|
||||
| `POST` | `/subscriptions` | Создать триальную или платную подписку.
|
||||
| `POST` | `/subscriptions/{id}/extend` | Продлить подписку на N дней.
|
||||
@@ -260,7 +260,7 @@ curl -X POST "http://127.0.0.1:8080/tokens" \
|
||||
1. **Health-check** — перед авторизацией UI вызывает `GET /health`, чтобы отобразить статус и версию бота.
|
||||
2. **Настройки UI** — подгружает категории через `GET /settings/categories`, далее выводит форму со значениями из `GET /settings`.
|
||||
3. **Статистика дашборда** — `GET /stats/overview` для карточек с показателями.
|
||||
4. **Раздел пользователи** — `GET /users` с поиском (`search`), фильтрами по статусу или промо-группе. Для детальной карточки использовать `GET /users/{id}`.
|
||||
4. **Раздел пользователи** — `GET /users` с поиском (`search`), фильтрами по статусу или промо-группе. Для детальной карточки использовать `GET /users/{id}` (в качестве ID можно использовать как внутренний user.id, так и telegram_id пользователя).
|
||||
5. **Операции с подпиской** — использовать `POST /subscriptions/{id}/...` эндпоинты для продления, выдачи трафика и устройств.
|
||||
6. **Поддержка** — список тикетов (`GET /tickets`), изменение статуса (`POST /tickets/{id}/status`), блокировка ответов (`POST /tickets/{id}/reply-block`).
|
||||
7. **История операций** — `GET /transactions` с фильтрами по пользователю, типу и периоду.
|
||||
|
||||
Reference in New Issue
Block a user