refactor(autorenewal): clean up imports and simplify error handling

This commit is contained in:
2026-05-18 17:58:34 +07:00
parent a9a650f37b
commit 1228ec8b56
9 changed files with 19 additions and 34 deletions

View File

@@ -10,9 +10,7 @@ from bot.texts import (
CALLBACK_FALLBACK,
FAQ_TEXT,
GENERAL_PROCESSING,
GENERAL_SUCCESS,
NO_SUBSCRIPTION,
NOTHING_PLACEHOLDER,
)
from db.models.users import User
from misc import utils

View File

@@ -1,4 +1,5 @@
import asyncio
import contextlib
import logging
from aiogram import Bot, Dispatcher
@@ -21,6 +22,7 @@ from services.renewal_service import RenewalService
from services.user_service import UserService
logging.basicConfig(level=logging.DEBUG)
contextlib.suppress(asyncio.CancelledError)
async def main():
@@ -78,10 +80,7 @@ async def main():
await dp.start_polling(bot)
finally:
scheduler_task.cancel()
try:
await scheduler_task
except asyncio.CancelledError:
pass
await scheduler_task
if __name__ == "__main__":

View File

@@ -1,7 +1,7 @@
import asyncio
import logging
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
from sqlalchemy.ext.asyncio import async_sessionmaker
from services.renewal_service import RenewalService

View File

@@ -227,4 +227,4 @@ AUTORENEWAL_INSUFFICIENT_FUNDS = (
AUTORENEWAL_SUCCESS = (
"<b>✅ Подписка продлена автоматически. Списано <code>{price}₽</code>.</b>"
"Новая дата окончания: <code>{end_date}</code>."
)
)

View File

@@ -1,5 +1,5 @@
from dotenv import load_dotenv
from pydantic import Field, SecretStr
from pydantic import Field
from pydantic_settings import BaseSettings
load_dotenv(override=True)
@@ -10,11 +10,11 @@ class Settings(BaseSettings):
"postgresql+asyncpg://malenia:skibidi@localhost:5432/malenia_db", alias="POSTGRES_URL"
)
bot_token: SecretStr = Field(alias="BOT_TOKEN")
bot_token: str = Field(alias="BOT_TOKEN")
admins: list[int] = Field(alias="ADMINS")
pally_shop_id: SecretStr = Field(alias="PALLY_SHOP_ID")
pally_token: SecretStr = Field(alias="PALLY_TOKEN")
pally_shop_id: str = Field(alias="PALLY_SHOP_ID")
pally_token: str = Field(alias="PALLY_TOKEN")
referal_bonus: int = Field(10, alias="REFERAL_BONUS")
deposit_threshold: int = Field(50)
@@ -23,7 +23,7 @@ class Settings(BaseSettings):
remnawave_url: str = Field(alias="REMNAWAVE_URL")
subscription_url: str = Field(alias="SUBSCRIPTION_URL")
remnawave_token: SecretStr = Field(alias="REMNAWAVE_TOKEN")
remnawave_token: str = Field(alias="REMNAWAVE_TOKEN")
min_devices: int = Field(3, alias="MIN_DEVICES")
max_devices: int = Field(12, alias="MAX_DEVICES")

View File

@@ -1,4 +1,4 @@
from datetime import datetime, timezone
from datetime import UTC, datetime, timedelta
from sqlalchemy import select, update
from sqlalchemy.ext.asyncio import AsyncSession
@@ -10,13 +10,12 @@ class SubscriptionRepository:
async def get_subscriptions_for_autorenewal(
self, session: AsyncSession, *, days_before_expiry: int = 1
) -> list[Subscription]:
from datetime import timedelta
threshold = datetime.now(timezone.utc) + timedelta(days=days_before_expiry)
threshold = datetime.now(UTC) + timedelta(days=days_before_expiry)
stmt = (
select(Subscription)
.where(
Subscription.autorenew == True,
Subscription.autorenew == True, # noqa: E712
Subscription.status == SubscriptionStatus.ACTIVE,
Subscription.end_date <= threshold,
)

View File

@@ -1,6 +1,5 @@
from dataclasses import dataclass
from aiogram import Bot
from remnawave import RemnawaveSDK
from misc.pally import PallyClient

View File

@@ -1,6 +1,8 @@
from dataclasses import dataclass
from pydantic import BaseModel
class NewUserDTO(BaseModel):
id: int
referal: int | None = None

View File

@@ -13,6 +13,7 @@ from misc import utils
from repositories.subscriptions import SubscriptionRepository
from repositories.users import UserRepository
from schemas.subscriptions import InternalSquads, SubscriptionPlan, SubscriptionStatus
from services import rw as rw_service
logger = logging.getLogger(__name__)
@@ -48,22 +49,14 @@ class RenewalService:
return utils.calculate_price(plan)
async def _renew_subscription_in_rw(self, subscription: Subscription) -> None:
from services import rw as rw_service
rw_user = await rw_service.get_user_by_telegram_id(self.rw_sdk, subscription.user_id)
if not rw_user:
raise RenewalServiceError(f"RW user not found for user_id={subscription.user_id}")
duration = datetime.timedelta(days=subscription.duration_days)
squads = [InternalSquads.DEFAULT]
if subscription.whitelists:
squads.append(InternalSquads.WHITELISTS)
if rw_user.expire_at > datetime.datetime.now(datetime.UTC):
new_expire = rw_user.expire_at + duration
else:
new_expire = datetime.datetime.now(datetime.UTC) + duration
await rw_service.add_days(self.rw_sdk, rw_user.uuid, subscription.duration_days)
await rw_service.update_squads(self.rw_sdk, rw_user.uuid, squads)
await rw_service.set_hwid_limit(self.rw_sdk, rw_user.uuid, subscription.devices)
@@ -95,10 +88,7 @@ class RenewalService:
await self.subscription_repository.update_subscription_autorenew(
session, user_id, False
)
await self._notify_user(
user_id,
AUTORENEWAL_INSUFFICIENT_FUNDS
)
await self._notify_user(user_id, AUTORENEWAL_INSUFFICIENT_FUNDS)
return False
result = await self.user_repository.decrease_user_balance(
@@ -143,7 +133,7 @@ class RenewalService:
user_id,
AUTORENEWAL_SUCCESS.format(
price=price,
end_date=new_end_date.strftime('%d.%m.%Y'),
end_date=new_end_date.strftime("%d.%m.%Y"),
),
)
@@ -171,9 +161,7 @@ class RenewalService:
else:
results["failed"] += 1
except Exception:
logger.exception(
"Error processing autorenewal for user %d", subscription.user_id
)
logger.exception("Error processing autorenewal for user %d", subscription.user_id)
results["failed"] += 1
logger.info(