chore: run lint and prettifier
This commit is contained in:
+132
-133
@@ -1,52 +1,51 @@
|
||||
import logging
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from aiogram import Bot
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import text
|
||||
from typing import Optional, Dict, Any
|
||||
from aiogram import Bot
|
||||
from datetime import datetime, timezone, timedelta
|
||||
|
||||
from config.settings import Settings
|
||||
from db.dal import user_dal
|
||||
from db.dal import payment_dal
|
||||
from db.models import User
|
||||
from db.dal import subscription_dal
|
||||
from bot.middlewares.i18n import JsonI18n
|
||||
from config.settings import Settings
|
||||
from db.dal import payment_dal, subscription_dal, user_dal
|
||||
|
||||
from .subscription_service import SubscriptionService
|
||||
|
||||
|
||||
class ReferralService:
|
||||
|
||||
def __init__(self, settings: Settings,
|
||||
subscription_service: SubscriptionService, bot: Bot,
|
||||
i18n: JsonI18n):
|
||||
def __init__(
|
||||
self,
|
||||
settings: Settings,
|
||||
subscription_service: SubscriptionService,
|
||||
bot: Bot,
|
||||
i18n: JsonI18n,
|
||||
):
|
||||
self.settings = settings
|
||||
self.subscription_service = subscription_service
|
||||
self.bot = bot
|
||||
self.i18n = i18n
|
||||
|
||||
async def apply_referral_bonuses_for_payment(
|
||||
self,
|
||||
session: AsyncSession,
|
||||
referee_user_id: int,
|
||||
purchased_subscription_months: int,
|
||||
current_payment_db_id: Optional[int] = None,
|
||||
skip_if_active_before_payment: bool = True) -> Dict[str, Any]:
|
||||
self,
|
||||
session: AsyncSession,
|
||||
referee_user_id: int,
|
||||
purchased_subscription_months: int,
|
||||
current_payment_db_id: Optional[int] = None,
|
||||
skip_if_active_before_payment: bool = True,
|
||||
) -> Dict[str, Any]:
|
||||
|
||||
referee_final_end_date: Optional[datetime] = None
|
||||
referee_bonus_applied_days: Optional[int] = None
|
||||
inviter_bonus_successfully_applied = False
|
||||
|
||||
try:
|
||||
referee_user_model = await user_dal.get_user_by_id(
|
||||
session, referee_user_id)
|
||||
referee_user_model = await user_dal.get_user_by_id(session, referee_user_id)
|
||||
if not referee_user_model or referee_user_model.referred_by_id is None:
|
||||
logging.debug(
|
||||
f"User {referee_user_id} not referred or inviter ID missing. No referral bonuses."
|
||||
)
|
||||
return {
|
||||
"referee_bonus_applied_days": None,
|
||||
"referee_new_end_date": None
|
||||
}
|
||||
return {"referee_bonus_applied_days": None, "referee_new_end_date": None}
|
||||
|
||||
# If configured to apply referral bonuses only once per invited user,
|
||||
# check if the referee already has succeeded payments.
|
||||
@@ -59,55 +58,63 @@ class ReferralService:
|
||||
)
|
||||
if succeeded_count and succeeded_count > 0:
|
||||
logging.info(
|
||||
f"Referral bonuses skipped for user {referee_user_id}: already has {succeeded_count} succeeded payments.")
|
||||
return {
|
||||
"referee_bonus_applied_days": None,
|
||||
"referee_new_end_date": None
|
||||
}
|
||||
f"Referral bonuses skipped for user {referee_user_id}: already has {succeeded_count} succeeded payments."
|
||||
)
|
||||
return {"referee_bonus_applied_days": None, "referee_new_end_date": None}
|
||||
except Exception as e_cnt:
|
||||
logging.error(f"Failed counting succeeded payments for user {referee_user_id}: {e_cnt}")
|
||||
logging.error(
|
||||
f"Failed counting succeeded payments for user {referee_user_id}: {e_cnt}"
|
||||
)
|
||||
|
||||
# Additionally, do not award referral bonuses if the user was active at payment time
|
||||
# (has an active subscription now). This avoids giving bonuses to already active users.
|
||||
if skip_if_active_before_payment:
|
||||
try:
|
||||
if await self.subscription_service.has_active_subscription(session, referee_user_id):
|
||||
if await self.subscription_service.has_active_subscription(
|
||||
session, referee_user_id
|
||||
):
|
||||
logging.info(
|
||||
f"Referral bonuses skipped for user {referee_user_id}: user currently has an active subscription.")
|
||||
return {
|
||||
"referee_bonus_applied_days": None,
|
||||
"referee_new_end_date": None
|
||||
}
|
||||
f"Referral bonuses skipped for user {referee_user_id}: user currently has an active subscription."
|
||||
)
|
||||
return {"referee_bonus_applied_days": None, "referee_new_end_date": None}
|
||||
except Exception as e_sub:
|
||||
logging.error(f"Failed to check active subscription for {referee_user_id}: {e_sub}")
|
||||
logging.error(
|
||||
f"Failed to check active subscription for {referee_user_id}: {e_sub}"
|
||||
)
|
||||
|
||||
inviter_user_id = referee_user_model.referred_by_id
|
||||
inviter_user_model = await user_dal.get_user_by_id(
|
||||
session, inviter_user_id)
|
||||
inviter_user_model = await user_dal.get_user_by_id(session, inviter_user_id)
|
||||
|
||||
referee_name_for_msg = referee_user_model.first_name or f"User {referee_user_id}"
|
||||
|
||||
default_lang_for_placeholder = self.settings.DEFAULT_LANGUAGE
|
||||
inviter_name_for_referee_msg = (
|
||||
inviter_user_model.first_name if inviter_user_model
|
||||
and inviter_user_model.first_name else self.i18n.gettext(
|
||||
default_lang_for_placeholder, "friend_placeholder"))
|
||||
inviter_user_model.first_name
|
||||
if inviter_user_model and inviter_user_model.first_name
|
||||
else self.i18n.gettext(default_lang_for_placeholder, "friend_placeholder")
|
||||
)
|
||||
|
||||
inviter_bonus_days = self.settings.referral_bonus_inviter.get(
|
||||
purchased_subscription_months)
|
||||
purchased_subscription_months
|
||||
)
|
||||
referee_bonus_days = self.settings.referral_bonus_referee.get(
|
||||
purchased_subscription_months)
|
||||
purchased_subscription_months
|
||||
)
|
||||
|
||||
if inviter_bonus_days and inviter_bonus_days > 0:
|
||||
if not inviter_user_model:
|
||||
|
||||
logging.warning(
|
||||
f"Inviter user {inviter_user_id} not found in local DB. Cannot apply inviter bonus."
|
||||
)
|
||||
else:
|
||||
|
||||
inviter_panel_uuid, inviter_panel_sub_link_id, _, _ = await self.subscription_service._get_or_create_panel_user_link_details(
|
||||
session, inviter_user_id, inviter_user_model)
|
||||
(
|
||||
inviter_panel_uuid,
|
||||
inviter_panel_sub_link_id,
|
||||
_,
|
||||
_,
|
||||
) = await self.subscription_service._get_or_create_panel_user_link_details(
|
||||
session, inviter_user_id, inviter_user_model
|
||||
)
|
||||
|
||||
if not inviter_panel_uuid:
|
||||
logging.warning(
|
||||
@@ -115,11 +122,13 @@ class ReferralService:
|
||||
)
|
||||
|
||||
else:
|
||||
new_end_date_inviter = await self.subscription_service.extend_active_subscription_days(
|
||||
session=session,
|
||||
user_id=inviter_user_id,
|
||||
bonus_days=inviter_bonus_days,
|
||||
reason=f"referral bonus from {referee_name_for_msg}"
|
||||
new_end_date_inviter = (
|
||||
await self.subscription_service.extend_active_subscription_days(
|
||||
session=session,
|
||||
user_id=inviter_user_id,
|
||||
bonus_days=inviter_bonus_days,
|
||||
reason=f"referral bonus from {referee_name_for_msg}",
|
||||
)
|
||||
)
|
||||
|
||||
if new_end_date_inviter:
|
||||
@@ -129,29 +138,30 @@ class ReferralService:
|
||||
)
|
||||
|
||||
try:
|
||||
inviter_lang = inviter_user_model.language_code or default_lang_for_placeholder
|
||||
_i = lambda k, **kw: self.i18n.gettext(
|
||||
inviter_lang, k, **kw)
|
||||
inviter_lang = (
|
||||
inviter_user_model.language_code or default_lang_for_placeholder
|
||||
)
|
||||
_i = lambda k, **kw: self.i18n.gettext(inviter_lang, k, **kw)
|
||||
await self.bot.send_message(
|
||||
inviter_user_id,
|
||||
_i("referral_bonus_inviter_notification_extended",
|
||||
days=inviter_bonus_days,
|
||||
referee_name=referee_name_for_msg,
|
||||
new_end_date=new_end_date_inviter.
|
||||
strftime('%Y-%m-%d')))
|
||||
_i(
|
||||
"referral_bonus_inviter_notification_extended",
|
||||
days=inviter_bonus_days,
|
||||
referee_name=referee_name_for_msg,
|
||||
new_end_date=new_end_date_inviter.strftime("%Y-%m-%d"),
|
||||
),
|
||||
)
|
||||
except Exception as e_notify_inviter:
|
||||
logging.error(
|
||||
f"Failed to send bonus notification to inviter {inviter_user_id}: {e_notify_inviter}"
|
||||
)
|
||||
else:
|
||||
|
||||
logging.info(
|
||||
f"Inviter {inviter_user_id} has no active sub to extend. Creating new bonus subscription for {inviter_bonus_days} days."
|
||||
)
|
||||
|
||||
bonus_start_date = datetime.now(timezone.utc)
|
||||
bonus_end_date = bonus_start_date + timedelta(
|
||||
days=inviter_bonus_days)
|
||||
bonus_end_date = bonus_start_date + timedelta(days=inviter_bonus_days)
|
||||
|
||||
if not inviter_panel_sub_link_id:
|
||||
logging.error(
|
||||
@@ -159,60 +169,56 @@ class ReferralService:
|
||||
)
|
||||
else:
|
||||
bonus_sub_payload = {
|
||||
"user_id":
|
||||
inviter_user_id,
|
||||
"panel_user_uuid":
|
||||
inviter_panel_uuid,
|
||||
"panel_subscription_uuid":
|
||||
inviter_panel_sub_link_id,
|
||||
"start_date":
|
||||
bonus_start_date,
|
||||
"end_date":
|
||||
bonus_end_date,
|
||||
"duration_months":
|
||||
0,
|
||||
"is_active":
|
||||
True,
|
||||
"status_from_panel":
|
||||
"ACTIVE_BONUS",
|
||||
"traffic_limit_bytes":
|
||||
self.settings.user_traffic_limit_bytes,
|
||||
"auto_renew_enabled":
|
||||
False,
|
||||
"user_id": inviter_user_id,
|
||||
"panel_user_uuid": inviter_panel_uuid,
|
||||
"panel_subscription_uuid": inviter_panel_sub_link_id,
|
||||
"start_date": bonus_start_date,
|
||||
"end_date": bonus_end_date,
|
||||
"duration_months": 0,
|
||||
"is_active": True,
|
||||
"status_from_panel": "ACTIVE_BONUS",
|
||||
"traffic_limit_bytes": self.settings.user_traffic_limit_bytes,
|
||||
"auto_renew_enabled": False,
|
||||
}
|
||||
try:
|
||||
await subscription_dal.deactivate_other_active_subscriptions(
|
||||
session, inviter_panel_uuid,
|
||||
inviter_panel_sub_link_id)
|
||||
session, inviter_panel_uuid, inviter_panel_sub_link_id
|
||||
)
|
||||
bonus_sub = await subscription_dal.upsert_subscription(
|
||||
session, bonus_sub_payload)
|
||||
session, bonus_sub_payload
|
||||
)
|
||||
|
||||
panel_update_success = await self.subscription_service.panel_service.update_user_details_on_panel(
|
||||
inviter_panel_uuid, {
|
||||
"expireAt":
|
||||
bonus_end_date.isoformat(
|
||||
timespec='milliseconds').
|
||||
replace('+00:00', 'Z'),
|
||||
"status":
|
||||
"ACTIVE",
|
||||
})
|
||||
inviter_panel_uuid,
|
||||
{
|
||||
"expireAt": bonus_end_date.isoformat(
|
||||
timespec="milliseconds"
|
||||
).replace("+00:00", "Z"),
|
||||
"status": "ACTIVE",
|
||||
},
|
||||
)
|
||||
if panel_update_success:
|
||||
inviter_bonus_successfully_applied = True
|
||||
logging.info(
|
||||
f"New bonus subscription for {inviter_bonus_days} days created for inviter {inviter_user_id}."
|
||||
)
|
||||
|
||||
inviter_lang = inviter_user_model.language_code or default_lang_for_placeholder
|
||||
inviter_lang = (
|
||||
inviter_user_model.language_code
|
||||
or default_lang_for_placeholder
|
||||
)
|
||||
_i = lambda k, **kw: self.i18n.gettext(
|
||||
inviter_lang, k, **kw)
|
||||
inviter_lang, k, **kw
|
||||
)
|
||||
await self.bot.send_message(
|
||||
inviter_user_id,
|
||||
_i("referral_bonus_inviter_notification_new_sub",
|
||||
days=inviter_bonus_days,
|
||||
referee_name=
|
||||
referee_name_for_msg,
|
||||
new_end_date=bonus_end_date.
|
||||
strftime('%Y-%m-%d')))
|
||||
_i(
|
||||
"referral_bonus_inviter_notification_new_sub",
|
||||
days=inviter_bonus_days,
|
||||
referee_name=referee_name_for_msg,
|
||||
new_end_date=bonus_end_date.strftime("%Y-%m-%d"),
|
||||
),
|
||||
)
|
||||
else:
|
||||
logging.warning(
|
||||
f"Failed to update panel for new bonus subscription for inviter {inviter_user_id}. Local bonus sub created (ID: {bonus_sub.subscription_id}) but may not be active on panel."
|
||||
@@ -221,16 +227,17 @@ class ReferralService:
|
||||
except Exception as e_create_bonus_sub:
|
||||
logging.error(
|
||||
f"Failed to create new bonus subscription for inviter {inviter_user_id}: {e_create_bonus_sub}",
|
||||
exc_info=True)
|
||||
exc_info=True,
|
||||
)
|
||||
|
||||
if referee_bonus_days and referee_bonus_days > 0:
|
||||
|
||||
new_end_date_referee = await self.subscription_service.extend_active_subscription_days(
|
||||
session=session,
|
||||
user_id=referee_user_id,
|
||||
bonus_days=referee_bonus_days,
|
||||
reason=
|
||||
f"referee bonus (invited by {inviter_name_for_referee_msg})"
|
||||
new_end_date_referee = (
|
||||
await self.subscription_service.extend_active_subscription_days(
|
||||
session=session,
|
||||
user_id=referee_user_id,
|
||||
bonus_days=referee_bonus_days,
|
||||
reason=f"referee bonus (invited by {inviter_name_for_referee_msg})",
|
||||
)
|
||||
)
|
||||
if new_end_date_referee:
|
||||
referee_final_end_date = new_end_date_referee
|
||||
@@ -239,7 +246,6 @@ class ReferralService:
|
||||
f"Bonus of {referee_bonus_days} days successfully applied to referee {referee_user_id}."
|
||||
)
|
||||
else:
|
||||
|
||||
logging.warning(
|
||||
f"Failed to apply referee bonus for {referee_user_id} (could not extend their new subscription)."
|
||||
)
|
||||
@@ -247,19 +253,19 @@ class ReferralService:
|
||||
return {
|
||||
"referee_bonus_applied_days": referee_bonus_applied_days,
|
||||
"referee_new_end_date": referee_final_end_date,
|
||||
"inviter_bonus_applied_flag":
|
||||
inviter_bonus_successfully_applied
|
||||
"inviter_bonus_applied_flag": inviter_bonus_successfully_applied,
|
||||
}
|
||||
except Exception as e:
|
||||
logging.error(
|
||||
f"Error in apply_referral_bonuses_for_payment for referee {referee_user_id}: {e}",
|
||||
exc_info=True)
|
||||
exc_info=True,
|
||||
)
|
||||
|
||||
raise
|
||||
|
||||
async def generate_referral_link(self, session: AsyncSession,
|
||||
bot_username: str,
|
||||
inviter_user_id: int) -> Optional[str]:
|
||||
async def generate_referral_link(
|
||||
self, session: AsyncSession, bot_username: str, inviter_user_id: int
|
||||
) -> Optional[str]:
|
||||
try:
|
||||
user = await user_dal.get_user_by_id(session, inviter_user_id)
|
||||
if not user:
|
||||
@@ -289,16 +295,15 @@ class ReferralService:
|
||||
|
||||
async def get_referral_stats(self, session: AsyncSession, user_id: int) -> dict:
|
||||
"""Get referral statistics for a user"""
|
||||
from db.dal import user_dal, payment_dal
|
||||
|
||||
|
||||
try:
|
||||
# Count total invited users (referrals)
|
||||
invited_count_result = await session.execute(
|
||||
text("SELECT COUNT(*) FROM users WHERE referred_by_id = :user_id"),
|
||||
{"user_id": user_id}
|
||||
{"user_id": user_id},
|
||||
)
|
||||
invited_count = invited_count_result.scalar() or 0
|
||||
|
||||
|
||||
# Count users who made successful payments (purchased subscription)
|
||||
purchased_count_result = await session.execute(
|
||||
text("""
|
||||
@@ -308,17 +313,11 @@ class ReferralService:
|
||||
WHERE u.referred_by_id = :user_id
|
||||
AND p.status = 'succeeded'
|
||||
"""),
|
||||
{"user_id": user_id}
|
||||
{"user_id": user_id},
|
||||
)
|
||||
purchased_count = purchased_count_result.scalar() or 0
|
||||
|
||||
return {
|
||||
"invited_count": invited_count,
|
||||
"purchased_count": purchased_count
|
||||
}
|
||||
|
||||
return {"invited_count": invited_count, "purchased_count": purchased_count}
|
||||
except Exception as e:
|
||||
logging.error(f"Error getting referral stats for user {user_id}: {e}")
|
||||
return {
|
||||
"invited_count": 0,
|
||||
"purchased_count": 0
|
||||
}
|
||||
return {"invited_count": 0, "purchased_count": 0}
|
||||
|
||||
Reference in New Issue
Block a user