198 lines
6.8 KiB
Python
198 lines
6.8 KiB
Python
from typing import Optional
|
|
from aiogram.types import Message, ReactionTypeEmoji
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from remnawave import RemnawaveSDK
|
|
|
|
from config import settings
|
|
from db.models.tickets import TicketStatus
|
|
from db.models.users import User
|
|
from schemas.tickets import NewTicketDTO
|
|
from schemas.users import NewUserDTO, TicketCreator
|
|
from repositories import UserRepository
|
|
from repositories.tickets import TicketRepository
|
|
from services.rw import RWUserInfo, get_user_by_telegram_id
|
|
from texts import (
|
|
ADMIN_STANDARD_FALLBACK,
|
|
ADMIN_TICKET_WITH_RW,
|
|
SUPPORT_SIGNATURE,
|
|
TICKET_STATUS_EMOJIS,
|
|
ADMIN_TICKET_WITHOUT_RW,
|
|
)
|
|
from misc import utils
|
|
|
|
|
|
class UserServiceException(Exception): ...
|
|
|
|
|
|
class UserService:
|
|
def __init__(
|
|
self, user_repository: UserRepository, ticket_repository: TicketRepository
|
|
) -> None:
|
|
self.user_repository = user_repository
|
|
self.ticket_repository = ticket_repository
|
|
|
|
@staticmethod
|
|
def format_ticket_name(
|
|
creator: TicketCreator,
|
|
status: Optional[TicketStatus] = TicketStatus.OPEN,
|
|
ticket_id: Optional[int] = None,
|
|
):
|
|
return (
|
|
TICKET_STATUS_EMOJIS[status.value]
|
|
+ (f" T-{ticket_id:0>3}" if ticket_id else "")
|
|
+ " | "
|
|
+ (f"@{creator.username}" if creator.username else str(creator.id))
|
|
)
|
|
|
|
@staticmethod
|
|
async def format_admin_support_message(
|
|
ticket_number: int, user: User, rw_user: Optional[RWUserInfo]
|
|
):
|
|
username_display = utils.format_username(user.username)
|
|
|
|
if rw_user is None:
|
|
# Пользователь не найден в Remnawave
|
|
return ADMIN_TICKET_WITHOUT_RW.format(
|
|
ticket_number=ticket_number,
|
|
full_name=user.full_name,
|
|
username_display=username_display,
|
|
telegram_id=user.id,
|
|
)
|
|
|
|
status_icon, status_text = utils.format_status(rw_user.status)
|
|
expire_date = rw_user.expire_at.strftime("%d.%m.%Y")
|
|
days_left = utils.format_days_left(rw_user.expire_at)
|
|
used_traffic = utils.format_traffic(
|
|
rw_user.used_traffic_bytes, rw_user.traffic_limit_bytes
|
|
)
|
|
hwid_limit = utils.format_hwid_limit(rw_user.hwid_device_limit)
|
|
squads = utils.format_squads(rw_user.active_squads)
|
|
|
|
return ADMIN_TICKET_WITH_RW.format(
|
|
ticket_number=ticket_number,
|
|
full_name=user.full_name,
|
|
username_display=username_display,
|
|
telegram_id=user.id,
|
|
status_icon=status_icon,
|
|
status=status_text,
|
|
expire_date=expire_date,
|
|
days_left=days_left,
|
|
used_traffic=used_traffic,
|
|
hwid_limit=hwid_limit,
|
|
squads=squads,
|
|
remnawave_uuid=rw_user.uuid,
|
|
)
|
|
|
|
async def add_user(
|
|
self, session: AsyncSession, user_id: int, referal: Optional[int] = None
|
|
) -> User:
|
|
user = await self.user_repository.get_user_by_id(session, user_id)
|
|
if user:
|
|
return user
|
|
|
|
referal = int(referal) if str(referal).isdigit() else None
|
|
model = NewUserDTO(id=user_id, referal=referal)
|
|
return await self.user_repository.create_user(session, model)
|
|
|
|
async def support_forward_message(
|
|
self, session: AsyncSession, msg: Message, rw: RemnawaveSDK
|
|
):
|
|
user = await self.user_repository.get_user_by_id(session, msg.from_user.id)
|
|
if not user:
|
|
raise UserServiceException("User %d is not found.", msg.from_user.id)
|
|
|
|
ticket = await self.ticket_repository.get_available_ticket_by_user_id(
|
|
session, user.id
|
|
)
|
|
|
|
if ticket:
|
|
try:
|
|
await msg.copy_to(
|
|
chat_id=settings.admin_group_id, message_thread_id=ticket.thread_id
|
|
)
|
|
return
|
|
except Exception:
|
|
await self.ticket_repository.update_ticket_status(
|
|
session, ticket, TicketStatus.CLOSED
|
|
)
|
|
raise
|
|
|
|
rw_user = await get_user_by_telegram_id(rw, user.id)
|
|
creator = TicketCreator(id=user.id, username=msg.from_user.username)
|
|
|
|
ticket_dto = NewTicketDTO(
|
|
creator_id=user.id,
|
|
)
|
|
ticket = await self.ticket_repository.create_ticket(
|
|
session, ticket_data=ticket_dto
|
|
)
|
|
|
|
thread = await msg.bot.create_forum_topic(
|
|
settings.admin_group_id,
|
|
self.format_ticket_name(
|
|
creator, ticket_id=ticket.id, status=TicketStatus.PENDING
|
|
),
|
|
)
|
|
ticket = await self.ticket_repository.update_ticket_thread_id(
|
|
session, ticket, thread.message_thread_id
|
|
)
|
|
|
|
await msg.bot.send_message(
|
|
settings.admin_group_id,
|
|
message_thread_id=thread.message_thread_id,
|
|
text=await self.format_admin_support_message(
|
|
ticket_number=ticket.id, user=msg.from_user, rw_user=rw_user
|
|
),
|
|
)
|
|
await msg.copy_to(
|
|
settings.admin_group_id, message_thread_id=thread.message_thread_id
|
|
)
|
|
|
|
async def support_answer(self, session: AsyncSession, msg: Message):
|
|
thread_id = msg.message_thread_id
|
|
ticket = await self.ticket_repository.get_ticket_by_thread_id(
|
|
session, thread_id=thread_id
|
|
)
|
|
|
|
if ticket.status == TicketStatus.PENDING:
|
|
await self.ticket_repository.update_ticket_status(
|
|
session, ticket, TicketStatus.OPEN
|
|
)
|
|
await msg.bot.edit_forum_topic(
|
|
settings.admin_group_id,
|
|
ticket.thread_id,
|
|
name=self.format_ticket_name(
|
|
TicketCreator(ticket.creator_id), ticket_id=ticket.id
|
|
),
|
|
)
|
|
|
|
try:
|
|
await msg.bot.send_message(ticket.creator_id, SUPPORT_SIGNATURE)
|
|
await msg.copy_to(ticket.creator_id)
|
|
await msg.react([ReactionTypeEmoji(emoji="⚡")])
|
|
except Exception:
|
|
await msg.reply(ADMIN_STANDARD_FALLBACK)
|
|
|
|
async def close_ticket(self, session: AsyncSession, msg: Message):
|
|
thread_id = msg.message_thread_id
|
|
ticket = await self.ticket_repository.get_ticket_by_thread_id(
|
|
session, thread_id=thread_id
|
|
)
|
|
|
|
await msg.bot.edit_forum_topic(
|
|
settings.admin_group_id,
|
|
ticket.thread_id,
|
|
name=self.format_ticket_name(
|
|
TicketCreator(ticket.creator_id),
|
|
ticket_id=ticket.id,
|
|
status=TicketStatus.CLOSED,
|
|
),
|
|
)
|
|
|
|
await self.ticket_repository.update_ticket_status(
|
|
session, ticket, TicketStatus.CLOSED
|
|
)
|
|
await msg.bot.close_forum_topic(
|
|
settings.admin_group_id, message_thread_id=thread_id
|
|
)
|