feat: add subscription and transaction repositories
- Implemented SubscriptionRepository with methods to get, create, and update subscriptions. - Added BalanceTXRepository for creating and retrieving balance transactions. - Introduced a test script for simulating Pally webhook for local testing.
This commit is contained in:
@@ -4,7 +4,7 @@ from datetime import UTC, datetime, timedelta
|
||||
from uuid import UUID
|
||||
|
||||
from remnawave import RemnawaveSDK
|
||||
from remnawave.models import UpdateUserRequestDto
|
||||
from remnawave.models import CreateUserRequestDto, UpdateUserRequestDto
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -319,3 +319,33 @@ async def update_squads(
|
||||
exc,
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
async def create_user(
|
||||
sdk: RemnawaveSDK,
|
||||
username: str,
|
||||
telegram_id: int,
|
||||
expire_at: datetime,
|
||||
hwid_device_limit: int | None,
|
||||
squad_uuids: list[str],
|
||||
) -> RWUserInfo | None:
|
||||
"""
|
||||
Создать нового пользователя в Remnawave.
|
||||
hwid_device_limit=None → без ограничений.
|
||||
Возвращает RWUserInfo при успехе, None при ошибке.
|
||||
"""
|
||||
try:
|
||||
dto = CreateUserRequestDto(
|
||||
username=username,
|
||||
telegram_id=telegram_id,
|
||||
expire_at=expire_at,
|
||||
hwid_device_limit=hwid_device_limit,
|
||||
active_internal_squads=[UUID(s) for s in squad_uuids] if squad_uuids else None,
|
||||
)
|
||||
result = await sdk.users.create_user(dto)
|
||||
if result is None:
|
||||
return None
|
||||
return _parse_user(result)
|
||||
except Exception as exc:
|
||||
logger.warning("Failed to create user username=%s: %s", username, exc)
|
||||
return None
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import datetime
|
||||
import logging
|
||||
|
||||
from aiogram.types import Message
|
||||
from aiogram.types import Message, User as TelegramUser
|
||||
from remnawave import RemnawaveSDK
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
@@ -12,8 +13,10 @@ from bot.texts import (
|
||||
from db.models.users import User
|
||||
from misc import utils
|
||||
from repositories import UserRepository
|
||||
from repositories.subscriptions import SubscriptionRepository
|
||||
from schemas.subscriptions import InternalSquads, SubscriptionPlan, SubscriptionStatus
|
||||
from schemas.users import NewUserDTO
|
||||
from services.rw import RWUserInfo, get_user_by_telegram_id
|
||||
from services import rw
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -22,12 +25,15 @@ class UserServiceError(Exception): ...
|
||||
|
||||
|
||||
class UserService:
|
||||
def __init__(self, user_repository: UserRepository) -> None:
|
||||
def __init__(
|
||||
self, user_repository: UserRepository, subscription_repository: SubscriptionRepository
|
||||
) -> None:
|
||||
self.user_repository = user_repository
|
||||
self.subscription_repository = subscription_repository
|
||||
|
||||
@staticmethod
|
||||
def format_subscription_message(
|
||||
rw_user: RWUserInfo | None,
|
||||
rw_user: rw.RWUserInfo | None,
|
||||
link: str | None = None,
|
||||
):
|
||||
if rw_user is None:
|
||||
@@ -62,7 +68,7 @@ class UserService:
|
||||
if user:
|
||||
return user
|
||||
|
||||
rw_user = await get_user_by_telegram_id(rw_sdk, user_id)
|
||||
rw_user = await rw.get_user_by_telegram_id(rw_sdk, user_id)
|
||||
link = utils.get_subscription_link(rw_user.short_uuid) if rw_user else None
|
||||
referal = int(referal) if str(referal).isdigit() and user_id != int(referal) else None
|
||||
|
||||
@@ -72,7 +78,7 @@ class UserService:
|
||||
async def update_user_link(
|
||||
self, session: AsyncSession, user_id: int, rw_sdk: RemnawaveSDK
|
||||
) -> User | None:
|
||||
rw_user = await get_user_by_telegram_id(rw_sdk, user_id)
|
||||
rw_user = await rw.get_user_by_telegram_id(rw_sdk, user_id)
|
||||
|
||||
user = await self.user_repository.get_user_by_id(session, user_id)
|
||||
|
||||
@@ -97,3 +103,98 @@ class UserService:
|
||||
logger.critical("failed to send a message to user %d.", user.id, exc_info=True)
|
||||
|
||||
return sent
|
||||
|
||||
async def buy_subscription(
|
||||
self,
|
||||
session: AsyncSession,
|
||||
subscription: SubscriptionPlan,
|
||||
rw_sdk: RemnawaveSDK,
|
||||
user: TelegramUser,
|
||||
):
|
||||
duration = utils.convert_duration_to_timedelta(subscription.duration)
|
||||
squads = [InternalSquads.DEFAULT]
|
||||
if subscription.whitelists:
|
||||
squads.append(InternalSquads.WHITELISTS)
|
||||
|
||||
## If subscription exists, add.
|
||||
rw_user = await rw.get_user_by_telegram_id(rw_sdk, user.id)
|
||||
if rw_user:
|
||||
db_subscription = await self.subscription_repository.get_subscription_by_user_id(
|
||||
session, user.id
|
||||
)
|
||||
if db_subscription:
|
||||
db_subscription = await self.subscription_repository.update_sub(
|
||||
session,
|
||||
user.id,
|
||||
end_date=rw_user.expire_at + duration,
|
||||
autorenew=False,
|
||||
status=SubscriptionStatus.ACTIVE,
|
||||
devices=int(subscription.devices),
|
||||
whitelists=subscription.whitelists,
|
||||
plan_price_paid=utils.calculate_price(subscription),
|
||||
duration_days=duration.days,
|
||||
)
|
||||
else:
|
||||
db_subscription = await self.subscription_repository.create_subscription(
|
||||
session,
|
||||
user_id=user.id,
|
||||
end_date=rw_user.expire_at + duration,
|
||||
autorenew=False,
|
||||
status=SubscriptionStatus.ACTIVE,
|
||||
devices=int(subscription.devices),
|
||||
whitelists=subscription.whitelists,
|
||||
plan_price_paid=utils.calculate_price(subscription),
|
||||
duration_days=duration.days,
|
||||
)
|
||||
|
||||
await rw.add_days(rw_sdk, rw_user.uuid, duration.days)
|
||||
await rw.update_squads(rw_sdk, rw_user.uuid, squads)
|
||||
await rw.set_hwid_limit(rw_sdk, rw_user.uuid, subscription.devices)
|
||||
|
||||
await self.update_user_link(session, user.id, rw_sdk)
|
||||
return db_subscription
|
||||
|
||||
# Create
|
||||
expire = datetime.datetime.now(datetime.UTC) + duration
|
||||
username = "tg_" + str(user.username or user.id)
|
||||
|
||||
rw_user = await rw.create_user(
|
||||
rw_sdk,
|
||||
username=username,
|
||||
telegram_id=user.id,
|
||||
expire_at=expire,
|
||||
hwid_device_limit=subscription.devices,
|
||||
squad_uuids=squads,
|
||||
)
|
||||
|
||||
db_subscription = await self.subscription_repository.get_subscription_by_user_id(
|
||||
session, user.id
|
||||
)
|
||||
if db_subscription:
|
||||
db_subscription = await self.subscription_repository.update_sub(
|
||||
session,
|
||||
user.id,
|
||||
end_date=expire,
|
||||
autorenew=False,
|
||||
status=SubscriptionStatus.ACTIVE,
|
||||
devices=int(subscription.devices),
|
||||
whitelists=subscription.whitelists,
|
||||
plan_price_paid=utils.calculate_price(subscription),
|
||||
duration_days=duration.days,
|
||||
)
|
||||
else:
|
||||
db_subscription = await self.subscription_repository.create_subscription(
|
||||
session,
|
||||
user_id=user.id,
|
||||
end_date=expire,
|
||||
autorenew=False,
|
||||
status=SubscriptionStatus.ACTIVE,
|
||||
devices=int(subscription.devices),
|
||||
whitelists=subscription.whitelists,
|
||||
plan_price_paid=utils.calculate_price(subscription),
|
||||
duration_days=duration.days,
|
||||
)
|
||||
|
||||
await self.update_user_link(session, user.id, rw_sdk)
|
||||
|
||||
return db_subscription
|
||||
|
||||
Reference in New Issue
Block a user