mirror of
https://github.com/System-End/nephthys.git
synced 2026-04-19 18:35:14 +00:00
Refactoring around macros (#111)
* Use transcripts for most macro message texts * Write a get_user_name() util * Remove Fraudpheus reference * Use get_user_name() in shipcertqueue.py * Always impersonate in reopen.py See 182771acdfd8eca0410f8c012f24e7cd51a71925 * Add user PFP method to slack_user.py * Handle the case of a username not being present * Use get_user_profile() to get user PFPs when tickets are (re)opened * Remove comment * Fix type error in reopen.py * Guard against ts being not present in reopen.py * Ensure consistent handling of profile_pic_512x() being None
This commit is contained in:
parent
f25a27afc7
commit
fdb729a00b
8 changed files with 97 additions and 70 deletions
|
|
@ -11,6 +11,7 @@ from nephthys.macros import run_macro
|
|||
from nephthys.utils.env import env
|
||||
from nephthys.utils.logging import send_heartbeat
|
||||
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.models import User
|
||||
|
|
@ -136,42 +137,25 @@ async def handle_new_question(
|
|||
client (AsyncWebClient): Slack API client.
|
||||
db_user (User | None): The database user object, or None if user doesn't exist yet.
|
||||
"""
|
||||
async with perf_timer("Slack user info fetch"):
|
||||
user = event.get("user", "unknown")
|
||||
author_id = event.get("user", "unknown")
|
||||
text = event.get("text", "")
|
||||
user_info_response = await client.users_info(user=user) or {}
|
||||
|
||||
user_info = user_info_response.get("user")
|
||||
if user_info:
|
||||
profile_pic: str | None = user_info["profile"].get("image_512", "")
|
||||
display_name: str = (
|
||||
user_info["profile"]["display_name"] or user_info["real_name"]
|
||||
)
|
||||
else:
|
||||
profile_pic = None
|
||||
display_name = "Explorer"
|
||||
async with perf_timer("Slack user info fetch"):
|
||||
author = await get_user_profile(author_id)
|
||||
|
||||
if db_user:
|
||||
async with perf_timer("Getting ticket count from DB"):
|
||||
past_tickets = await env.db.ticket.count(where={"openedById": db_user.id})
|
||||
else:
|
||||
past_tickets = 0
|
||||
username = (user_info or {}).get(
|
||||
"name"
|
||||
) # this should never actually be empty but if it is, that is a major issue
|
||||
|
||||
username = author.display_name()
|
||||
async with perf_timer("Creating user in DB"):
|
||||
if not username:
|
||||
await send_heartbeat(
|
||||
f"SOMETHING HAS GONE TERRIBLY WRONG <@{user}> has no username found - <@{env.slack_maintainer_id}>"
|
||||
)
|
||||
db_user = await env.db.user.upsert(
|
||||
where={
|
||||
"slackId": user,
|
||||
"slackId": author_id,
|
||||
},
|
||||
data={
|
||||
"create": {"slackId": user, "username": username},
|
||||
"update": {"slackId": user, "username": username},
|
||||
"create": {"slackId": author_id, "username": username},
|
||||
"update": {"slackId": author_id, "username": username},
|
||||
},
|
||||
)
|
||||
|
||||
|
|
@ -180,8 +164,8 @@ async def handle_new_question(
|
|||
event,
|
||||
client,
|
||||
past_tickets=past_tickets,
|
||||
display_name=display_name,
|
||||
profile_pic=profile_pic,
|
||||
display_name=author.display_name(),
|
||||
profile_pic=author.profile_pic_512x() or "",
|
||||
)
|
||||
|
||||
ticket_message_ts = ticket_message["ts"]
|
||||
|
|
@ -190,9 +174,9 @@ async def handle_new_question(
|
|||
return
|
||||
|
||||
user_facing_message_text = (
|
||||
env.transcript.first_ticket_create.replace("(user)", display_name)
|
||||
env.transcript.first_ticket_create.replace("(user)", author.display_name())
|
||||
if past_tickets == 0
|
||||
else env.transcript.ticket_create.replace("(user)", display_name)
|
||||
else env.transcript.ticket_create.replace("(user)", author.display_name())
|
||||
)
|
||||
ticket_url = f"https://hackclub.slack.com/archives/{env.slack_ticket_channel}/p{ticket_message_ts.replace('.', '')}"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from nephthys.actions.resolve import resolve
|
||||
from nephthys.macros.types import Macro
|
||||
from nephthys.utils.env import env
|
||||
from nephthys.utils.slack_user import get_user_profile
|
||||
from nephthys.utils.ticket_methods import reply_to_ticket
|
||||
|
||||
|
||||
|
|
@ -14,14 +15,9 @@ class FAQ(Macro):
|
|||
sender = await env.db.user.find_first(where={"id": ticket.openedById})
|
||||
if not sender:
|
||||
return
|
||||
user_info = await env.slack_client.users_info(user=sender.slackId)
|
||||
name = (
|
||||
user_info["user"]["profile"].get("display_name")
|
||||
or user_info["user"]["profile"].get("real_name")
|
||||
or user_info["user"]["name"]
|
||||
)
|
||||
user = await get_user_profile(sender.slackId)
|
||||
await reply_to_ticket(
|
||||
text=f"hey, {name}! this question is answered in the faq i sent earlier, please make sure to check it out! :rac_cute:\n\n<{env.transcript.faq_link}|here it is again>",
|
||||
text=env.transcript.faq_macro.replace("(user)", user.display_name()),
|
||||
ticket=ticket,
|
||||
client=env.slack_client,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from nephthys.actions.resolve import resolve
|
||||
from nephthys.macros.types import Macro
|
||||
from nephthys.utils.env import env
|
||||
from nephthys.utils.slack_user import get_user_profile
|
||||
from nephthys.utils.ticket_methods import reply_to_ticket
|
||||
|
||||
|
||||
|
|
@ -9,19 +10,14 @@ class Fraud(Macro):
|
|||
|
||||
async def run(self, ticket, helper, **kwargs):
|
||||
"""
|
||||
A simple macro telling people to use Fraudpheus
|
||||
A simple macro telling people to DM the Fraud Squad
|
||||
"""
|
||||
sender = await env.db.user.find_first(where={"id": ticket.openedById})
|
||||
if not sender:
|
||||
return
|
||||
user_info = await env.slack_client.users_info(user=sender.slackId)
|
||||
name = (
|
||||
user_info["user"]["profile"].get("display_name")
|
||||
or user_info["user"]["profile"].get("real_name")
|
||||
or user_info["user"]["name"]
|
||||
)
|
||||
user = await get_user_profile(sender.slackId)
|
||||
await reply_to_ticket(
|
||||
text=f"Hiya {name}! Would you mind directing any fraud related queries to <@U091HC53CE8>? :rac_cute:\n\nIt'll keep your case confidential and make it easier for the fraud team to keep track of!",
|
||||
text=env.transcript.fraud_macro.replace("(user)", user.display_name()),
|
||||
ticket=ticket,
|
||||
client=env.slack_client,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from nephthys.actions.resolve import resolve
|
||||
from nephthys.macros.types import Macro
|
||||
from nephthys.utils.env import env
|
||||
from nephthys.utils.slack_user import get_user_profile
|
||||
from nephthys.utils.ticket_methods import reply_to_ticket
|
||||
|
||||
|
||||
|
|
@ -14,14 +15,9 @@ class Identity(Macro):
|
|||
sender = await env.db.user.find_first(where={"id": ticket.openedById})
|
||||
if not sender:
|
||||
return
|
||||
user_info = await env.slack_client.users_info(user=sender.slackId)
|
||||
name = (
|
||||
user_info["user"]["profile"].get("display_name")
|
||||
or user_info["user"]["profile"].get("real_name")
|
||||
or user_info["user"]["name"]
|
||||
)
|
||||
user = await get_user_profile(sender.slackId)
|
||||
await reply_to_ticket(
|
||||
text=f"hey, {name}! please could you ask questions about identity verification in <#{env.transcript.identity_help_channel}>? :rac_cute:\n\nit helps the verification team keep track of questions easier!",
|
||||
text=env.transcript.identity_macro.replace("(user)", user.display_name()),
|
||||
ticket=ticket,
|
||||
client=env.slack_client,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import logging
|
||||
|
||||
from nephthys.macros.types import Macro
|
||||
from nephthys.utils.env import env
|
||||
from nephthys.utils.logging import send_heartbeat
|
||||
from nephthys.utils.slack_user import get_user_profile
|
||||
from nephthys.utils.ticket_methods import reply_to_ticket
|
||||
from prisma.enums import TicketStatus
|
||||
|
||||
|
|
@ -31,20 +34,18 @@ class Reopen(Macro):
|
|||
client=env.slack_client,
|
||||
)
|
||||
|
||||
user_info = await env.slack_client.users_info(user=ticket.openedBy.slackId)
|
||||
name = (
|
||||
user_info["user"]["profile"]["display_name"]
|
||||
or user_info["user"]["real_name"]
|
||||
or "Explorer"
|
||||
if not ticket.openedBy:
|
||||
await send_heartbeat(
|
||||
f"Attempted to reopen ticket (TS {ticket.msgTs}) but ticket author has not been recorded"
|
||||
)
|
||||
profile_pic = user_info["user"]["profile"].get("image_512", "")
|
||||
return
|
||||
author_id = ticket.openedBy.slackId
|
||||
author = await get_user_profile(author_id)
|
||||
thread_url = f"https://hackclub.slack.com/archives/{env.slack_help_channel}/p{ticket.msgTs.replace('.', '')}"
|
||||
|
||||
use_impersonation = await env.workspace_admin_available()
|
||||
|
||||
backend_message = await env.slack_client.chat_postMessage(
|
||||
channel=env.slack_ticket_channel,
|
||||
text=f"Reopened ticket from <@{ticket.openedBy.slackId}>: {ticket.description}",
|
||||
text=f"Reopened ticket from <@{author_id}>: {ticket.description}",
|
||||
blocks=[
|
||||
{
|
||||
"type": "input",
|
||||
|
|
@ -65,18 +66,21 @@ class Reopen(Macro):
|
|||
"elements": [
|
||||
{
|
||||
"type": "mrkdwn",
|
||||
"text": f"Reopened by <@{helper.slackId}>. Originally submitted by <@{ticket.openedBy.slackId}>. <{thread_url}|View thread>.",
|
||||
"text": f"Reopened by <@{helper.slackId}>. Originally submitted by <@{author_id}>. <{thread_url}|View thread>.",
|
||||
}
|
||||
],
|
||||
},
|
||||
],
|
||||
username=name if use_impersonation else None,
|
||||
icon_url=profile_pic if use_impersonation else None,
|
||||
username=author.display_name(),
|
||||
icon_url=author.profile_pic_512x() or "",
|
||||
unfurl_links=True,
|
||||
unfurl_media=True,
|
||||
)
|
||||
|
||||
new_ticket_ts = backend_message["ts"]
|
||||
if not new_ticket_ts:
|
||||
logging.error(f"Invalid Slack message creation response: {backend_message}")
|
||||
raise ValueError("Invalid Slack message creation response: no ts")
|
||||
await env.db.ticket.update(
|
||||
where={"id": ticket.id},
|
||||
data={"ticketTs": new_ticket_ts},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from nephthys.actions.resolve import resolve
|
||||
from nephthys.macros.types import Macro
|
||||
from nephthys.utils.env import env
|
||||
from nephthys.utils.slack_user import get_user_profile
|
||||
from nephthys.utils.ticket_methods import reply_to_ticket
|
||||
|
||||
|
||||
|
|
@ -14,14 +15,9 @@ class ShipCertQueue(Macro):
|
|||
sender = await env.db.user.find_first(where={"id": ticket.openedById})
|
||||
if not sender:
|
||||
return
|
||||
user_info = await env.slack_client.users_info(user=sender.slackId)
|
||||
name = (
|
||||
user_info["user"]["profile"].get("display_name")
|
||||
or user_info["user"]["profile"].get("real_name")
|
||||
or user_info["user"]["name"]
|
||||
)
|
||||
user = await get_user_profile(sender.slackId)
|
||||
await reply_to_ticket(
|
||||
text=f"Hi {name}! Unfortunately, there is a backlog of projects awaiting ship certification; please be patient. \n\n *pssst... voting more will move your project further towards the front of the queue.*",
|
||||
text=f"Hi {user.display_name()}! Unfortunately, there is a backlog of projects awaiting ship certification; please be patient. \n\n *pssst... voting more will move your project further towards the front of the queue.*",
|
||||
ticket=ticket,
|
||||
client=env.slack_client,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -77,6 +77,19 @@ class Transcript(BaseModel):
|
|||
default="hey! please keep your messages *all in one thread* to make it easier to read! i've gone ahead and removed that message from the channel for ya :D",
|
||||
)
|
||||
|
||||
faq_macro: str = Field(
|
||||
default="", description="Message to be sent when the FAQ macro is used"
|
||||
)
|
||||
|
||||
fraud_macro: str = Field(
|
||||
default="Hiya (user)! Would you mind directing any fraud related queries to <@U091HC53CE8>? :rac_cute:\n\nIt'll keep your case confidential and make it easier for the fraud team to keep track of!",
|
||||
description="Message to be sent when the fraud macro is used",
|
||||
)
|
||||
|
||||
identity_macro: str = Field(
|
||||
default="", description="Message to be sent when the identity macro is used"
|
||||
)
|
||||
|
||||
home_unknown_user_title: str = Field(
|
||||
default=":upside-down_orpheus: woah, stop right there {name}!",
|
||||
description="Title for unknown user on home page",
|
||||
|
|
@ -134,6 +147,12 @@ if your question has been answered, please hit the button below to mark it as re
|
|||
self.ticket_resolve_stale = f""":rac_nooo: it looks like this post is a bit old! if you still need help, please make a new post in <#{self.help_channel}> and someone'll be happy to help you out! ^~^
|
||||
"""
|
||||
|
||||
if not self.faq_macro:
|
||||
self.faq_macro = f"hey, (user)! this question is answered in the faq i sent earlier, please make sure to check it out! :rac_cute:\n\n<{self.faq_link}|here it is again>"
|
||||
|
||||
if not self.identity_macro:
|
||||
self.identity_macro = f"hey, (user)! please could you ask questions about identity verification in <#{self.identity_help_channel}>? :rac_cute:\n\nit helps the verification team keep track of questions easier!"
|
||||
|
||||
if not self.home_unknown_user_text:
|
||||
self.home_unknown_user_text = f"heyyyy, heidi here! it looks like i'm not allowed to show ya this. sorry! if you think this is a mistake, please reach out to <@{self.program_owner}> and she'll lmk what to do!"
|
||||
|
||||
|
|
|
|||
36
nephthys/utils/slack_user.py
Normal file
36
nephthys/utils/slack_user.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import logging
|
||||
|
||||
from slack_sdk.web.async_slack_response import AsyncSlackResponse
|
||||
|
||||
from nephthys.utils.env import env
|
||||
|
||||
|
||||
class UserProfileWrapper:
|
||||
def __init__(self, users_info_response: AsyncSlackResponse):
|
||||
user_data = users_info_response.get("user")
|
||||
if not user_data:
|
||||
raise ValueError(f"Slack user not found: {users_info_response}")
|
||||
self.raw_data = user_data
|
||||
|
||||
def display_name(self) -> str:
|
||||
display_name = (
|
||||
self.raw_data["profile"].get("display_name")
|
||||
or self.raw_data["profile"].get("real_name")
|
||||
or self.raw_data["name"]
|
||||
)
|
||||
# This should never actually be empty but if it is, that is a major issue
|
||||
if not display_name:
|
||||
logging.error(
|
||||
f"SOMETHING HAS GONE TERRIBLY WRONG - user has no username: {self.raw_data}"
|
||||
)
|
||||
return "" # idk
|
||||
return display_name
|
||||
|
||||
def profile_pic_512x(self) -> str | None:
|
||||
return self.raw_data["profile"].get("image_512")
|
||||
|
||||
|
||||
async def get_user_profile(slack_id: str) -> UserProfileWrapper:
|
||||
"""Retrieve the user's display name from Slack given their Slack ID."""
|
||||
response = await env.slack_client.users_info(user=slack_id)
|
||||
return UserProfileWrapper(response)
|
||||
Loading…
Add table
Reference in a new issue