RemnaWave-bot Update
This commit is contained in:
@@ -1,11 +1,41 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
|
||||
|
||||
from app.db.base import utcnow
|
||||
from app.db.models import SupportTicket
|
||||
|
||||
OPEN_SUPPORT_TICKET_STATUS = "OPEN"
|
||||
ANSWERED_SUPPORT_TICKET_STATUS = "ANSWERED"
|
||||
CLOSED_SUPPORT_TICKET_STATUS = "CLOSED"
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class AdminSupportTicketListItem:
|
||||
id: int
|
||||
public_id: str
|
||||
telegram_id: int
|
||||
username: str | None
|
||||
display_name: str
|
||||
status: str
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
closed_at: datetime | None
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class AdminSupportTicketsPage:
|
||||
items: list[AdminSupportTicketListItem] = field(default_factory=list)
|
||||
page: int = 1
|
||||
page_size: int = 8
|
||||
total_items: int = 0
|
||||
total_pages: int = 1
|
||||
|
||||
|
||||
class SupportTicketService:
|
||||
def __init__(self, *, session_factory: async_sessionmaker[AsyncSession]) -> None:
|
||||
@@ -33,13 +63,17 @@ class SupportTicketService:
|
||||
support_chat_id=support_chat_id,
|
||||
support_thread_id=support_thread_id,
|
||||
support_message_id=support_message_id,
|
||||
status="OPEN",
|
||||
status=OPEN_SUPPORT_TICKET_STATUS,
|
||||
)
|
||||
session.add(ticket)
|
||||
await session.commit()
|
||||
await session.refresh(ticket)
|
||||
return ticket
|
||||
|
||||
async def get_ticket(self, *, ticket_id: int) -> SupportTicket | None:
|
||||
async with self._session_factory() as session:
|
||||
return await session.get(SupportTicket, ticket_id)
|
||||
|
||||
async def get_ticket_by_support_message(
|
||||
self,
|
||||
*,
|
||||
@@ -54,19 +88,121 @@ class SupportTicketService:
|
||||
)
|
||||
)
|
||||
|
||||
async def mark_answered(self, *, ticket_id: int) -> None:
|
||||
async def mark_answered(self, *, ticket_id: int) -> SupportTicket | None:
|
||||
async with self._session_factory() as session:
|
||||
ticket = await session.get(SupportTicket, ticket_id)
|
||||
if ticket is None:
|
||||
return
|
||||
return None
|
||||
|
||||
if ticket.status != CLOSED_SUPPORT_TICKET_STATUS:
|
||||
ticket.status = ANSWERED_SUPPORT_TICKET_STATUS
|
||||
|
||||
ticket.status = "ANSWERED"
|
||||
ticket.updated_at = utcnow()
|
||||
await session.commit()
|
||||
await session.refresh(ticket)
|
||||
return ticket
|
||||
|
||||
async def append_note(
|
||||
self,
|
||||
*,
|
||||
ticket_id: int,
|
||||
note_text: str,
|
||||
status: str | None = None,
|
||||
) -> SupportTicket | None:
|
||||
cleaned_note = note_text.strip()
|
||||
if not cleaned_note:
|
||||
return None
|
||||
|
||||
async with self._session_factory() as session:
|
||||
ticket = await session.get(SupportTicket, ticket_id)
|
||||
if ticket is None:
|
||||
return None
|
||||
|
||||
if ticket.support_note:
|
||||
ticket.support_note = f"{ticket.support_note}\n\n{cleaned_note}"
|
||||
else:
|
||||
ticket.support_note = cleaned_note
|
||||
if status and ticket.status != CLOSED_SUPPORT_TICKET_STATUS:
|
||||
ticket.status = status
|
||||
ticket.updated_at = utcnow()
|
||||
await session.commit()
|
||||
await session.refresh(ticket)
|
||||
return ticket
|
||||
|
||||
async def close_ticket(
|
||||
self,
|
||||
*,
|
||||
ticket_id: int,
|
||||
closed_by_telegram_id: int | None,
|
||||
) -> SupportTicket | None:
|
||||
async with self._session_factory() as session:
|
||||
ticket = await session.get(SupportTicket, ticket_id)
|
||||
if ticket is None:
|
||||
return None
|
||||
|
||||
ticket.status = CLOSED_SUPPORT_TICKET_STATUS
|
||||
ticket.closed_by_telegram_id = closed_by_telegram_id
|
||||
ticket.closed_at = utcnow()
|
||||
ticket.updated_at = utcnow()
|
||||
await session.commit()
|
||||
await session.refresh(ticket)
|
||||
return ticket
|
||||
|
||||
async def get_open_tickets_count(self) -> int:
|
||||
async with self._session_factory() as session:
|
||||
total = await session.scalar(
|
||||
select(func.count(SupportTicket.id)).where(SupportTicket.status == "OPEN")
|
||||
select(func.count(SupportTicket.id)).where(
|
||||
SupportTicket.status != CLOSED_SUPPORT_TICKET_STATUS
|
||||
)
|
||||
)
|
||||
return int(total or 0)
|
||||
|
||||
async def get_admin_tickets_page(
|
||||
self,
|
||||
*,
|
||||
page: int,
|
||||
page_size: int = 8,
|
||||
) -> AdminSupportTicketsPage:
|
||||
resolved_page_size = max(1, min(page_size, 20))
|
||||
|
||||
async with self._session_factory() as session:
|
||||
total_items = int(
|
||||
await session.scalar(select(func.count(SupportTicket.id))) or 0
|
||||
)
|
||||
total_pages = max(1, math.ceil(total_items / resolved_page_size)) if total_items else 1
|
||||
resolved_page = min(max(page, 1), total_pages)
|
||||
|
||||
rows = list(
|
||||
(
|
||||
await session.scalars(
|
||||
select(SupportTicket)
|
||||
.order_by(
|
||||
SupportTicket.created_at.desc(),
|
||||
SupportTicket.id.desc(),
|
||||
)
|
||||
.offset((resolved_page - 1) * resolved_page_size)
|
||||
.limit(resolved_page_size)
|
||||
)
|
||||
).all()
|
||||
)
|
||||
|
||||
return AdminSupportTicketsPage(
|
||||
items=[
|
||||
AdminSupportTicketListItem(
|
||||
id=row.id,
|
||||
public_id=row.public_id,
|
||||
telegram_id=row.telegram_id,
|
||||
username=row.username,
|
||||
display_name=row.display_name,
|
||||
status=row.status,
|
||||
created_at=row.created_at,
|
||||
updated_at=row.updated_at,
|
||||
closed_at=row.closed_at,
|
||||
)
|
||||
for row in rows
|
||||
],
|
||||
page=resolved_page,
|
||||
page_size=resolved_page_size,
|
||||
total_items=total_items,
|
||||
total_pages=total_pages,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user