Only notify segment changes when amount has changed

This commit is contained in:
Dries Augustyns
2025-12-08 09:26:36 +01:00
parent 82e846e993
commit 6746d9e843
2 changed files with 70 additions and 71 deletions
+10 -3
View File
@@ -44,9 +44,16 @@ async function processProjectSegments(projectId: string, projectName?: string):
`[SEGMENT-COUNT-WORKER] Segment "${segment.name}": +${result.added} entries, -${result.removed} exits, ${result.total} total members`, `[SEGMENT-COUNT-WORKER] Segment "${segment.name}": +${result.added} entries, -${result.removed} exits, ${result.total} total members`,
); );
// Notify about segment membership update (only for tracked segments) // Notify about segment membership update only if there were actual changes
if (projectName) { if (projectName && (result.added > 0 || result.removed > 0)) {
await NtfyService.notifySegmentMembershipComputed(segment.name, projectName, projectId, result.total); await NtfyService.notifySegmentMembershipComputed(
segment.name,
projectName,
projectId,
result.total,
result.added,
result.removed,
);
} }
} catch (error) { } catch (error) {
signale.error(`[SEGMENT-COUNT-WORKER] Failed to compute membership for segment ${segment.id}:`, error); signale.error(`[SEGMENT-COUNT-WORKER] Failed to compute membership for segment ${segment.id}:`, error);
+60 -68
View File
@@ -43,24 +43,6 @@ export class NtfyService {
private static ntfyUrl: string | null = null; private static ntfyUrl: string | null = null;
private static isConfigured = false; private static isConfigured = false;
/**
* Initialize the ntfy service with the configured URL
*/
private static initialize(): void {
if (this.isConfigured) {
return;
}
this.ntfyUrl = process.env.NTFY_URL || null;
this.isConfigured = true;
if (!this.ntfyUrl) {
signale.warn('[NTFY] NTFY_URL not configured - notifications will be skipped');
} else {
signale.info(`[NTFY] Initialized with URL: ${this.ntfyUrl}`);
}
}
/** /**
* Send a notification to ntfy * Send a notification to ntfy
* @param notification - The notification details * @param notification - The notification details
@@ -77,7 +59,7 @@ export class NtfyService {
try { try {
const headers: Record<string, string> = { const headers: Record<string, string> = {
'Content-Type': 'text/plain', 'Content-Type': 'text/plain',
Title: notification.title, 'Title': notification.title,
}; };
if (notification.priority) { if (notification.priority) {
@@ -153,8 +135,6 @@ export class NtfyService {
}); });
} }
// ===== Event-specific notification helpers =====
/** /**
* Notify about a new project creation * Notify about a new project creation
*/ */
@@ -166,6 +146,8 @@ export class NtfyService {
); );
} }
// ===== Event-specific notification helpers =====
/** /**
* Notify about subscription started * Notify about subscription started
*/ */
@@ -216,11 +198,10 @@ export class NtfyService {
* Notify about payment failure * Notify about payment failure
*/ */
public static async notifyPaymentFailed(projectName: string, projectId: string): Promise<void> { public static async notifyPaymentFailed(projectName: string, projectId: string): Promise<void> {
await this.sendHigh( await this.sendHigh('Payment Failed', `Payment failed for project "${projectName}" (${projectId})`, [
'Payment Failed', NtfyTag.WARNING,
`Payment failed for project "${projectName}" (${projectId})`, NtfyTag.MONEY,
[NtfyTag.WARNING, NtfyTag.MONEY], ]);
);
} }
/** /**
@@ -251,21 +232,15 @@ export class NtfyService {
* Notify about project deletion - DEFAULT priority * Notify about project deletion - DEFAULT priority
*/ */
public static async notifyProjectDeleted(projectName: string, projectId: string, userId: string): Promise<void> { public static async notifyProjectDeleted(projectName: string, projectId: string, userId: string): Promise<void> {
await this.sendDefault( await this.sendDefault('Project Deleted', `Project "${projectName}" (${projectId}) was deleted by user ${userId}`, [
'Project Deleted', NtfyTag.WARNING,
`Project "${projectName}" (${projectId}) was deleted by user ${userId}`, ]);
[NtfyTag.WARNING],
);
} }
/** /**
* Notify about security warning (non-critical) * Notify about security warning (non-critical)
*/ */
public static async notifySecurityWarning( public static async notifySecurityWarning(projectName: string, projectId: string, warnings: string[]): Promise<void> {
projectName: string,
projectId: string,
warnings: string[],
): Promise<void> {
const warningText = warnings.join(', '); const warningText = warnings.join(', ');
await this.sendDefault( await this.sendDefault(
'Security Warning', 'Security Warning',
@@ -338,9 +313,7 @@ export class NtfyService {
projectCounts.sort((a, b) => b.count - a.count); projectCounts.sort((a, b) => b.count - a.count);
// Build breakdown message // Build breakdown message
const breakdown = projectCounts const breakdown = projectCounts.map(p => `${p.name} (${p.projectId}): ${p.count}`).join('\n');
.map((p) => `${p.name} (${p.projectId}): ${p.count}`)
.join('\n');
const bounceInfo = bounceType ? ` (${bounceType})` : ''; const bounceInfo = bounceType ? ` (${bounceType})` : '';
await this.send({ await this.send({
@@ -395,8 +368,6 @@ export class NtfyService {
}); });
} }
// ===== Campaign event notifications =====
/** /**
* Notify about campaign created (draft) - LOW priority * Notify about campaign created (draft) - LOW priority
*/ */
@@ -413,6 +384,8 @@ export class NtfyService {
}); });
} }
// ===== Campaign event notifications =====
/** /**
* Notify about campaign scheduled - DEFAULT priority * Notify about campaign scheduled - DEFAULT priority
*/ */
@@ -495,8 +468,6 @@ export class NtfyService {
}); });
} }
// ===== Workflow event notifications =====
/** /**
* Notify about workflow created - LOW priority * Notify about workflow created - LOW priority
*/ */
@@ -513,6 +484,8 @@ export class NtfyService {
}); });
} }
// ===== Workflow event notifications =====
/** /**
* Notify about workflow enabled * Notify about workflow enabled
*/ */
@@ -576,8 +549,6 @@ export class NtfyService {
); );
} }
// ===== Domain verification notifications =====
/** /**
* Notify about domain added * Notify about domain added
*/ */
@@ -589,6 +560,8 @@ export class NtfyService {
); );
} }
// ===== Domain verification notifications =====
/** /**
* Notify about domain verified * Notify about domain verified
*/ */
@@ -626,8 +599,6 @@ export class NtfyService {
); );
} }
// ===== Billing and usage limit notifications =====
/** /**
* Notify about billing limit approaching (80% threshold) * Notify about billing limit approaching (80% threshold)
*/ */
@@ -646,6 +617,8 @@ export class NtfyService {
); );
} }
// ===== Billing and usage limit notifications =====
/** /**
* Notify about billing limit exceeded - MAX priority (blocks operations) * Notify about billing limit exceeded - MAX priority (blocks operations)
*/ */
@@ -663,16 +636,10 @@ export class NtfyService {
); );
} }
// ===== API key notifications =====
/** /**
* Notify about API keys regenerated * Notify about API keys regenerated
*/ */
public static async notifyApiKeysRegenerated( public static async notifyApiKeysRegenerated(projectName: string, projectId: string, userId: string): Promise<void> {
projectName: string,
projectId: string,
userId: string,
): Promise<void> {
await this.sendHigh( await this.sendHigh(
'API Keys Regenerated', 'API Keys Regenerated',
`API keys for project "${projectName}" (${projectId}) were regenerated by user ${userId}`, `API keys for project "${projectName}" (${projectId}) were regenerated by user ${userId}`,
@@ -680,7 +647,7 @@ export class NtfyService {
); );
} }
// ===== Contact import notifications ===== // ===== API key notifications =====
/** /**
* Notify about contact import started - MIN priority (routine, high volume) * Notify about contact import started - MIN priority (routine, high volume)
@@ -699,6 +666,8 @@ export class NtfyService {
}); });
} }
// ===== Contact import notifications =====
/** /**
* Notify about contact import completed * Notify about contact import completed
*/ */
@@ -734,16 +703,10 @@ export class NtfyService {
); );
} }
// ===== Segment notifications =====
/** /**
* Notify about segment created - MIN priority * Notify about segment created - MIN priority
*/ */
public static async notifySegmentCreated( public static async notifySegmentCreated(segmentName: string, projectName: string, projectId: string): Promise<void> {
segmentName: string,
projectName: string,
projectId: string,
): Promise<void> {
await this.send({ await this.send({
title: 'Segment Created', title: 'Segment Created',
message: `Segment "${segmentName}" created in project "${projectName}" (${projectId})`, message: `Segment "${segmentName}" created in project "${projectName}" (${projectId})`,
@@ -752,6 +715,8 @@ export class NtfyService {
}); });
} }
// ===== Segment notifications =====
/** /**
* Notify about segment membership computed - LOW priority * Notify about segment membership computed - LOW priority
*/ */
@@ -760,10 +725,23 @@ export class NtfyService {
projectName: string, projectName: string,
projectId: string, projectId: string,
memberCount: number, memberCount: number,
added?: number,
removed?: number,
): Promise<void> { ): Promise<void> {
let message = `Segment "${segmentName}" in project "${projectName}" (${projectId}) now has ${memberCount} members`;
if (added !== undefined && removed !== undefined) {
const changes: string[] = [];
if (added > 0) changes.push(`+${added} added`);
if (removed > 0) changes.push(`-${removed} removed`);
if (changes.length > 0) {
message += ` (${changes.join(', ')})`;
}
}
await this.send({ await this.send({
title: 'Segment Membership Updated', title: 'Segment Membership Updated',
message: `Segment "${segmentName}" in project "${projectName}" (${projectId}) now has ${memberCount} members`, message,
priority: NtfyPriority.LOW, priority: NtfyPriority.LOW,
tags: [NtfyTag.CHART], tags: [NtfyTag.CHART],
}); });
@@ -772,11 +750,7 @@ export class NtfyService {
/** /**
* Notify about segment deleted - MIN priority * Notify about segment deleted - MIN priority
*/ */
public static async notifySegmentDeleted( public static async notifySegmentDeleted(segmentName: string, projectName: string, projectId: string): Promise<void> {
segmentName: string,
projectName: string,
projectId: string,
): Promise<void> {
await this.send({ await this.send({
title: 'Segment Deleted', title: 'Segment Deleted',
message: `Segment "${segmentName}" was deleted from project "${projectName}" (${projectId})`, message: `Segment "${segmentName}" was deleted from project "${projectName}" (${projectId})`,
@@ -784,4 +758,22 @@ export class NtfyService {
tags: [NtfyTag.INFO], tags: [NtfyTag.INFO],
}); });
} }
/**
* Initialize the ntfy service with the configured URL
*/
private static initialize(): void {
if (this.isConfigured) {
return;
}
this.ntfyUrl = process.env.NTFY_URL || null;
this.isConfigured = true;
if (!this.ntfyUrl) {
signale.warn('[NTFY] NTFY_URL not configured - notifications will be skipped');
} else {
signale.info(`[NTFY] Initialized with URL: ${this.ntfyUrl}`);
}
}
} }