From 14784f77d1a2fe8688159b2572b3d8c793f74280 Mon Sep 17 00:00:00 2001 From: Mish Date: Sat, 29 Nov 2025 11:25:49 +0000 Subject: [PATCH] Preparation for improving the daily summary message (#124) * Add lastMsgAt and lastMsgBy to DB * Keep track of the last messages in threads * Add UserType enum to schema * Make both of the new fields optional * Add data/ to .gitignore * Only include users who have resolved tickets on the overall leaderboard Fixes #96 * Fix docstring --- .gitignore | 3 +++ nephthys/events/message.py | 35 +++++++++++++++++++++++++---------- nephthys/tasks/daily_stats.py | 3 ++- prisma/schema.prisma | 25 +++++++++++++++++-------- 4 files changed, 47 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index ed87fe1..d20ab48 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,6 @@ wheels/ # Environment files .env + +# Local development +data/ \ No newline at end of file diff --git a/nephthys/events/message.py b/nephthys/events/message.py index 12c75a2..b418e4a 100644 --- a/nephthys/events/message.py +++ b/nephthys/events/message.py @@ -15,6 +15,7 @@ from nephthys.utils.performance import perf_timer from nephthys.utils.slack_user import get_user_profile from nephthys.utils.ticket_methods import delete_and_clean_up_ticket from prisma.enums import TicketStatus +from prisma.enums import UserType from prisma.models import User # Message subtypes that should be handled by on_message (messages with no subtype are always handled) @@ -46,22 +47,19 @@ async def handle_message_sent_to_channel(event: Dict[str, Any], client: AsyncWeb async def handle_message_in_thread(event: Dict[str, Any], db_user: User | None): """Handle a message sent in a help thread. - - Ignores non-helper messages. - - If the message starts with "?", run the corresponding macro. - - Otherwise, update the assigned helper and ticket status. + - If the message starts with "?" (and is from a helper), run the corresponding macro. + - Otherwise, update the assigned helper, ticket status, and lastMsg fields. """ - if not (db_user and db_user.helper): - return ticket_message = await env.db.ticket.find_first( where={"msgTs": event["thread_ts"]}, include={"openedBy": True, "tagsOnTickets": True}, ) if not ticket_message: return - text = event.get("text", "") - first_word = text.split()[0].lower() + text: str = event.get("text", "") + first_word = text.split()[0].lower() if text.strip() else "" - if first_word[0] == "?": + if db_user and db_user.helper and first_word.startswith("?"): await run_macro( name=first_word.lstrip("?"), ticket=ticket_message, @@ -71,8 +69,25 @@ async def handle_message_in_thread(event: Dict[str, Any], db_user: User | None): ) return - # A helper has sent a normal reply - if ticket_message.status != TicketStatus.CLOSED: + # Update lastMsg fields in DB + is_author = bool( + ticket_message.openedBy and event["user"] == ticket_message.openedBy.slackId + ) + is_helper = bool(db_user and db_user.helper) + await env.db.ticket.update( + where={"msgTs": event["thread_ts"]}, + data={ + "lastMsgAt": datetime.now(), + "lastMsgBy": UserType.AUTHOR + if is_author + else UserType.HELPER + if is_helper + else UserType.OTHER, + }, + ) + + # Ensure the ticket is assigned to the helper who last sent a message + if db_user and db_user.helper and ticket_message.status != TicketStatus.CLOSED: await env.db.ticket.update( where={"msgTs": event["thread_ts"]}, data={ diff --git a/nephthys/tasks/daily_stats.py b/nephthys/tasks/daily_stats.py index 0975e4e..ce05135 100644 --- a/nephthys/tasks/daily_stats.py +++ b/nephthys/tasks/daily_stats.py @@ -31,7 +31,8 @@ async def send_daily_stats(): try: tickets = await env.db.ticket.find_many() or [] users_with_closed_tickets = await env.db.user.find_many( - include={"closedTickets": True}, where={"helper": True} + include={"closedTickets": True}, + where={"helper": True, "closedTickets": {"some": {}}}, ) total_open = len([t for t in tickets if t.status == TicketStatus.OPEN]) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index ecca778..a4d240d 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -15,6 +15,12 @@ enum TicketStatus { CLOSED } +enum UserType { + AUTHOR + HELPER + OTHER +} + model User { id Int @id @unique @default(autoincrement()) slackId String @unique @@ -45,14 +51,17 @@ model Ticket { // Messages sent by the bot in the help thread userFacingMsgs BotMessage[] - openedBy User @relation("OpenedTickets", fields: [openedById], references: [id]) + lastMsgAt DateTime? @default(now()) + lastMsgBy UserType? @default(AUTHOR) + + openedBy User @relation("OpenedTickets", fields: [openedById], references: [id]) openedById Int closedBy User? @relation("ClosedTickets", fields: [closedById], references: [id]) closedById Int? closedAt DateTime? - assignedTo User? @relation("AssignedTickets", fields: [assignedToId], references: [id]) + assignedTo User? @relation("AssignedTickets", fields: [assignedToId], references: [id]) assignedToId Int? assignedAt DateTime? @@ -68,7 +77,7 @@ model Tag { ticketsOnTags TagsOnTickets[] userSubscriptions UserTagSubscription[] - createdAt DateTime @default(now()) + createdAt DateTime @default(now()) } model TagsOnTickets { @@ -84,9 +93,9 @@ model TagsOnTickets { } model UserTagSubscription { - user User @relation(fields: [userId], references: [id], onDelete: Cascade) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) userId Int - tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade) + tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade) tagId Int subscribedAt DateTime @default(now()) @@ -96,11 +105,11 @@ model UserTagSubscription { } model BotMessage { - id Int @id @unique @default(autoincrement()) + id Int @id @unique @default(autoincrement()) ts String channelId String - ticket Ticket @relation(fields: [ticketId], references: [id], onDelete: Cascade) + ticket Ticket @relation(fields: [ticketId], references: [id], onDelete: Cascade) ticketId Int @@unique([ts, channelId]) -} \ No newline at end of file +}