ktg-plugin-marketplace/plugins/linkedin-thought-leadership/scripts/analytics/src/utils/alerts.ts
Kjell Tore Guttormsen 39f8b275a6 feat(linkedin-thought-leadership): v1.0.0 — initial open-source import
Build LinkedIn thought leadership with algorithmic understanding,
strategic consistency, and AI-assisted content creation. Updated for
the January 2026 360Brew algorithm change.

16 agents, 25 commands, 6 skills, 9 hooks, 24 reference docs.

Personal data sanitized: voice samples generalized to template,
high-engagement posts cleared, region-specific references replaced
with placeholders.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 22:09:03 +02:00

162 lines
5.8 KiB
TypeScript

import type {
PostAnalytics,
Alert,
PostMetrics,
} from "../models/types.js";
import { ALERT_THRESHOLDS } from "../models/types.js";
import {
mean,
deviationsFromMean,
percentChange,
} from "./stats.js";
/**
* Analyze posts for spikes and drops based on standard deviation thresholds.
* For each post, checks if its metric value deviates significantly from the mean.
* Returns array of alerts sorted by severity (critical first).
*/
export function detectAlerts(
posts: PostAnalytics[],
metricKey: keyof PostMetrics = "impressions"
): Alert[] {
if (posts.length === 0) return [];
const alerts: Alert[] = [];
// Extract metric values
const values = posts.map((post) => post.metrics[metricKey]);
const avg = mean(values);
// Check each post for significant deviations
for (const post of posts) {
const value = post.metrics[metricKey];
const deviations = deviationsFromMean(value, values);
// Spike detection
if (deviations > ALERT_THRESHOLDS.spike) {
alerts.push({
type: "spike",
severity: "info",
metric: metricKey,
message: `Post "${post.title}" has unusually high ${metricKey}: ${value.toLocaleString()} (${deviations.toFixed(1)} std deviations above mean)`,
postId: post.id,
value,
baseline: avg,
deviations,
});
}
// Drop detection
if (deviations < ALERT_THRESHOLDS.drop) {
alerts.push({
type: "drop",
severity: "warning",
metric: metricKey,
message: `Post "${post.title}" has unusually low ${metricKey}: ${value.toLocaleString()} (${Math.abs(deviations).toFixed(1)} std deviations below mean)`,
postId: post.id,
value,
baseline: avg,
deviations,
});
}
}
// Sort by severity: critical > warning > info
const severityOrder = { critical: 0, warning: 1, info: 2 };
alerts.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);
return alerts;
}
/**
* Compare week-over-week metrics and generate alerts for significant changes.
* Uses percentChange and ALERT_THRESHOLDS for weekly drops and spikes.
*/
export function detectWeeklyAlerts(
currentWeekMetrics: { impressions: number; engagementRate: number },
previousWeekMetrics: { impressions: number; engagementRate: number }
): Alert[] {
const alerts: Alert[] = [];
// Analyze impressions
const impressionChange = percentChange(
currentWeekMetrics.impressions,
previousWeekMetrics.impressions
);
if (impressionChange < ALERT_THRESHOLDS.weeklyDropCritical) {
alerts.push({
type: "drop",
severity: "critical",
metric: "impressions",
message: `Critical drop in weekly impressions: ${impressionChange.toFixed(1)}% (from ${previousWeekMetrics.impressions.toLocaleString()} to ${currentWeekMetrics.impressions.toLocaleString()})`,
value: currentWeekMetrics.impressions,
baseline: previousWeekMetrics.impressions,
deviations: impressionChange / 10, // Rough conversion to deviations
});
} else if (impressionChange < ALERT_THRESHOLDS.weeklyDropWarning) {
alerts.push({
type: "drop",
severity: "warning",
metric: "impressions",
message: `Weekly impressions dropped by ${Math.abs(impressionChange).toFixed(1)}%: from ${previousWeekMetrics.impressions.toLocaleString()} to ${currentWeekMetrics.impressions.toLocaleString()}`,
value: currentWeekMetrics.impressions,
baseline: previousWeekMetrics.impressions,
deviations: impressionChange / 10,
});
} else if (impressionChange > ALERT_THRESHOLDS.weeklySpikeInfo) {
alerts.push({
type: "spike",
severity: "info",
metric: "impressions",
message: `Strong growth in weekly impressions: +${impressionChange.toFixed(1)}% (from ${previousWeekMetrics.impressions.toLocaleString()} to ${currentWeekMetrics.impressions.toLocaleString()})`,
value: currentWeekMetrics.impressions,
baseline: previousWeekMetrics.impressions,
deviations: impressionChange / 10,
});
}
// Analyze engagement rate
const engagementChange = percentChange(
currentWeekMetrics.engagementRate,
previousWeekMetrics.engagementRate
);
if (engagementChange < ALERT_THRESHOLDS.weeklyDropCritical) {
alerts.push({
type: "drop",
severity: "critical",
metric: "engagementRate",
message: `Critical drop in weekly engagement rate: ${engagementChange.toFixed(1)}% (from ${previousWeekMetrics.engagementRate.toFixed(2)}% to ${currentWeekMetrics.engagementRate.toFixed(2)}%)`,
value: currentWeekMetrics.engagementRate,
baseline: previousWeekMetrics.engagementRate,
deviations: engagementChange / 10,
});
} else if (engagementChange < ALERT_THRESHOLDS.weeklyDropWarning) {
alerts.push({
type: "drop",
severity: "warning",
metric: "engagementRate",
message: `Weekly engagement rate dropped by ${Math.abs(engagementChange).toFixed(1)}%: from ${previousWeekMetrics.engagementRate.toFixed(2)}% to ${currentWeekMetrics.engagementRate.toFixed(2)}%`,
value: currentWeekMetrics.engagementRate,
baseline: previousWeekMetrics.engagementRate,
deviations: engagementChange / 10,
});
} else if (engagementChange > ALERT_THRESHOLDS.weeklySpikeInfo) {
alerts.push({
type: "spike",
severity: "info",
metric: "engagementRate",
message: `Strong growth in weekly engagement rate: +${engagementChange.toFixed(1)}% (from ${previousWeekMetrics.engagementRate.toFixed(2)}% to ${currentWeekMetrics.engagementRate.toFixed(2)}%)`,
value: currentWeekMetrics.engagementRate,
baseline: previousWeekMetrics.engagementRate,
deviations: engagementChange / 10,
});
}
// Sort by severity: critical > warning > info
const severityOrder = { critical: 0, warning: 1, info: 2 };
alerts.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);
return alerts;
}