Normalize notification events and update grab flow

Introduce a NotificationEventConfig interface and validate NOTIFICATION_EVENTS with `satisfies` for stronger typing and normalized metadata shape. Replace escaped emoji sequences with literal emoji, simplify helper functions (getEventMeta/getEventTitle) to use the typed registry, and clean up titleByRequestType typing.

In download-torrent.processor: include the requesting user when setting status to downloading to avoid an extra DB query, and use that returned user to enqueue a non-blocking `request_grabbed` notification.

Docs: note that `request_grabbed` notifications are opt-in for existing backends. Tests: add messageLabel rendering tests for Apprise and ntfy providers to validate emoji, label text, and type-specific titles.
This commit is contained in:
kikootwo
2026-05-14 15:57:15 -04:00
parent 4ded2cf219
commit 5e4a38a340
5 changed files with 157 additions and 52 deletions
@@ -31,13 +31,16 @@ export async function processDownloadTorrent(payload: DownloadTorrentPayload): P
try {
// Update request status to downloading
await prisma.request.update({
const request = await prisma.request.update({
where: { id: requestId },
data: {
status: 'downloading',
progress: 0,
updatedAt: new Date(),
},
include: {
user: { select: { plexUsername: true } },
},
});
// Detect protocol from result and get appropriate client
@@ -103,30 +106,20 @@ export async function processDownloadTorrent(payload: DownloadTorrentPayload): P
logger.info(`Created download history record: ${downloadHistory.id}`);
// Send grab notification
const requestWithUser = await prisma.request.findUnique({
where: { id: requestId },
include: {
user: { select: { plexUsername: true } },
},
});
// Send grab notification (non-blocking — failures here don't fail the download)
const jobQueue = getJobQueueService();
if (requestWithUser) {
const grabMessage = `${torrent.title} via ${torrent.indexer} (${client.clientType})`;
await jobQueue.addNotificationJob(
'request_grabbed',
requestId,
audiobook.title,
audiobook.author,
requestWithUser.user.plexUsername || 'Unknown User',
grabMessage,
requestWithUser.type
).catch((error) => {
logger.error('Failed to queue grab notification', { error: error instanceof Error ? error.message : String(error) });
});
}
const grabMessage = `${torrent.title} via ${torrent.indexer} (${client.clientType})`;
await jobQueue.addNotificationJob(
'request_grabbed',
requestId,
audiobook.title,
audiobook.author,
request.user.plexUsername || 'Unknown User',
grabMessage,
request.type
).catch((error) => {
logger.error('Failed to queue grab notification', { error: error instanceof Error ? error.message : String(error) });
});
// Trigger monitor download job with initial delay
await jobQueue.addMonitorJob(