(data: {
recipientUserId: string;
actorUserId?: string;
type: string;
referenceId?: string;
referenceType?: string;
title: string;
url?: string;
})
| 12 | * Fire-and-forget safe — errors are logged but never thrown. |
| 13 | */ |
| 14 | export async function notifyUser(data: { |
| 15 | recipientUserId: string; |
| 16 | actorUserId?: string; |
| 17 | type: string; |
| 18 | referenceId?: string; |
| 19 | referenceType?: string; |
| 20 | title: string; |
| 21 | url?: string; |
| 22 | }): Promise<void> { |
| 23 | try { |
| 24 | // Don't notify users about their own actions |
| 25 | if (data.actorUserId && data.recipientUserId === data.actorUserId) { |
| 26 | return; |
| 27 | } |
| 28 | |
| 29 | await notificationDb.createNotification(data); |
| 30 | |
| 31 | // Send Slack DM if user has a linked Slack account |
| 32 | const slackMapping = await slackDb.getByWorkosUserId(data.recipientUserId); |
| 33 | if (slackMapping?.slack_user_id) { |
| 34 | const baseUrl = process.env.BASE_URL || 'https://agenticadvertising.org'; |
| 35 | const fullUrl = data.url ? `${baseUrl}${data.url}` : undefined; |
| 36 | const text = fullUrl ? `${data.title}\n<${fullUrl}|View>` : data.title; |
| 37 | |
| 38 | await sendDirectMessage(slackMapping.slack_user_id, { text }); |
| 39 | } |
| 40 | } catch (error) { |
| 41 | logger.error({ err: error, data }, 'Failed to send notification'); |
| 42 | } |
| 43 | } |
no test coverage detected