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
This commit is contained in:
Mish 2025-11-29 11:25:49 +00:00 committed by GitHub
parent 2fc6a90a4d
commit 14784f77d1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 47 additions and 19 deletions

3
.gitignore vendored
View file

@ -14,3 +14,6 @@ wheels/
# Environment files
.env
# Local development
data/

View file

@ -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={

View file

@ -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])

View file

@ -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])
}
}