# ruff: noqa: F401,F403,F405,I001 from ._runtime import * # noqa: F403,F405 from bot.services.support_service import TicketForbidden, TicketNotFound, TicketRateLimited from db.dal import support_dal, user_dal from db.models import SupportTicket, SupportTicketMessage def _support_ticket_payload(ticket: SupportTicket) -> Dict[str, Any]: return { "ticket_id": ticket.ticket_id, "user_id": ticket.user_id, "subject": ticket.subject, "category": ticket.category, "priority": ticket.priority, "status": ticket.status, "assigned_admin_id": ticket.assigned_admin_id, "last_message_at": ticket.last_message_at.isoformat() if ticket.last_message_at else None, "last_message_role": ticket.last_message_role, "unread_user_count": int(ticket.unread_user_count or 0), "unread_admin_count": int(ticket.unread_admin_count or 0), "created_at": ticket.created_at.isoformat() if ticket.created_at else None, "updated_at": ticket.updated_at.isoformat() if ticket.updated_at else None, "closed_at": ticket.closed_at.isoformat() if ticket.closed_at else None, } def _support_message_payload(message: SupportTicketMessage) -> Dict[str, Any]: return { "message_id": message.message_id, "ticket_id": message.ticket_id, "author_role": message.author_role, "author_user_id": message.author_user_id, "body": message.body, "is_internal_note": bool(message.is_internal_note), "created_at": message.created_at.isoformat() if message.created_at else None, "read_by_user_at": message.read_by_user_at.isoformat() if message.read_by_user_at else None, "read_by_admin_at": message.read_by_admin_at.isoformat() if message.read_by_admin_at else None, } def _support_limit_offset(request: web.Request) -> tuple[int, int]: limit = max(1, min(100, int(request.query.get("limit", 25) or 25))) offset = max(0, int(request.query.get("offset", 0) or 0)) return limit, offset async def support_tickets_route(request: web.Request) -> web.Response: user_id = _require_user_id(request) limit, offset = _support_limit_offset(request) status_filter = request.query.get("status") or None async_session_factory: sessionmaker = request.app["async_session_factory"] async with async_session_factory() as session: tickets = await support_dal.list_user_tickets( session, user_id, limit=limit, offset=offset, status_filter=status_filter, ) counts = await support_dal.user_ticket_counts(session, user_id) return web.json_response( { "ok": True, "tickets": [_support_ticket_payload(t) for t in tickets], "counts": counts, } ) async def support_create_ticket_route(request: web.Request) -> web.Response: user_id = _require_user_id(request) payload, error = _validate_model_payload(CreateTicketPayload, await _read_json(request)) if error: return error service = request.app["support_service"] try: ticket = await service.create_ticket( user_id, payload.subject, payload.category, payload.priority, payload.body, ) except TicketForbidden: return _json_error(403, "ticket_forbidden", "Support ticket action is forbidden") except TicketRateLimited: return _json_error(429, "ticket_rate_limited", "Too many support tickets") return web.json_response({"ok": True, "ticket": _support_ticket_payload(ticket)}) async def support_ticket_detail_route(request: web.Request) -> web.Response: user_id = _require_user_id(request) ticket_id = int(request.match_info["id"]) async_session_factory: sessionmaker = request.app["async_session_factory"] async with async_session_factory() as session: ticket, messages = await support_dal.get_ticket(session, ticket_id, include_internal=False) if not ticket or ticket.user_id != user_id: return _json_error(404, "not_found", "Ticket not found") return web.json_response( { "ok": True, "ticket": _support_ticket_payload(ticket), "messages": [_support_message_payload(m) for m in messages], } ) async def support_ticket_reply_route(request: web.Request) -> web.Response: user_id = _require_user_id(request) ticket_id = int(request.match_info["id"]) payload, error = _validate_model_payload(TicketReplyPayload, await _read_json(request)) if error: return error service = request.app["support_service"] try: ticket, message = await service.reply_as_user(user_id, ticket_id, payload.body) except TicketForbidden: return _json_error(403, "ticket_forbidden", "Support ticket action is forbidden") except TicketNotFound: return _json_error(404, "not_found", "Ticket not found") return web.json_response( { "ok": True, "ticket": _support_ticket_payload(ticket), "message": _support_message_payload(message), } ) async def support_ticket_read_route(request: web.Request) -> web.Response: user_id = _require_user_id(request) ticket_id = int(request.match_info["id"]) service = request.app["support_service"] try: await service.mark_read_as_user(user_id, ticket_id) except TicketNotFound: return _json_error(404, "not_found", "Ticket not found") return web.json_response({"ok": True}) async def support_unread_route(request: web.Request) -> web.Response: user_id = _require_user_id(request) async_session_factory: sessionmaker = request.app["async_session_factory"] async with async_session_factory() as session: user = await user_dal.get_user_by_id(session, user_id) if user and user.is_banned: return _json_error(403, "ticket_forbidden", "Support ticket action is forbidden") unread = await support_dal.count_user_unread(session, user_id) return web.json_response({"ok": True, "unread": unread})