Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | 8x 13x 13x 3x 1x 3x 3x 6x 6x 6x 6x 8x | // SPDX-FileCopyrightText: 2024-2026 Hack23 AB
// SPDX-License-Identifier: Apache-2.0
/**
* @module Generators/Strategies/BreakingNewsStrategy
* @description Article strategy for the Breaking News article type.
* Fetches OSINT intelligence signals (voting anomalies, coalition dynamics,
* voting report) and renders a real-time alert article.
*/
import type { EuropeanParliamentMCPClient } from '../../mcp/ep-mcp-client.js';
import { ArticleCategory } from '../../types/index.js';
import type { LanguageCode } from '../../types/index.js';
import { BREAKING_NEWS_TITLES, getLocalizedString } from '../../constants/languages.js';
import {
fetchVotingAnomalies,
fetchCoalitionDynamics,
fetchVotingReport,
} from '../pipeline/fetch-stage.js';
import { buildBreakingNewsContent } from '../breaking-content.js';
import type { ArticleStrategy, ArticleData, ArticleMetadata } from './article-strategy.js';
/** Keywords shared by all Breaking News articles */
const BREAKING_NEWS_KEYWORDS = [
'European Parliament',
'breaking news',
'voting anomalies',
'coalition dynamics',
] as const;
// ─── Data payload ─────────────────────────────────────────────────────────────
/** Data fetched and pre-processed by {@link BreakingNewsStrategy} */
export interface BreakingNewsArticleData extends ArticleData {
/** Raw voting anomaly text from MCP */
readonly anomalyRaw: string;
/** Raw coalition dynamics text from MCP */
readonly coalitionRaw: string;
/** Raw voting statistics report text from MCP */
readonly reportRaw: string;
}
// ─── Strategy implementation ──────────────────────────────────────────────────
/**
* Article strategy for {@link ArticleCategory.BREAKING_NEWS}.
* Aggregates OSINT signals from MCP and surfaces political intelligence.
*/
export class BreakingNewsStrategy implements ArticleStrategy<BreakingNewsArticleData> {
readonly type = ArticleCategory.BREAKING_NEWS;
readonly requiredMCPTools = [
'detect_voting_anomalies',
'analyze_coalition_dynamics',
'generate_report',
] as const;
/**
* Fetch all OSINT signals in parallel.
*
* @param client - MCP client or null
* @param date - ISO 8601 publication date
* @returns Populated breaking news data payload
*/
async fetchData(
client: EuropeanParliamentMCPClient | null,
date: string
): Promise<BreakingNewsArticleData> {
if (client) {
console.log(' 📡 Fetching OSINT intelligence data from MCP...');
}
const [anomalyRaw, coalitionRaw, reportRaw] = await Promise.all([
fetchVotingAnomalies(client),
fetchCoalitionDynamics(client),
fetchVotingReport(client),
]);
return { date, anomalyRaw, coalitionRaw, reportRaw };
}
/**
* Build the breaking news HTML body for the specified language.
*
* @param data - Breaking news data payload
* @param lang - Target language code used for editorial strings
* @returns Article HTML body
*/
buildContent(data: BreakingNewsArticleData, lang: LanguageCode): string {
return buildBreakingNewsContent(
data.date,
data.anomalyRaw,
data.coalitionRaw,
data.reportRaw,
'',
lang
);
}
/**
* Return language-specific metadata for the breaking news article.
*
* @param data - Breaking news data payload
* @param lang - Target language code
* @returns Localised metadata
*/
getMetadata(data: BreakingNewsArticleData, lang: LanguageCode): ArticleMetadata {
const titleFn = getLocalizedString(BREAKING_NEWS_TITLES, lang);
const { title, subtitle } = titleFn(data.date);
return {
title,
subtitle,
keywords: [...BREAKING_NEWS_KEYWORDS],
category: ArticleCategory.BREAKING_NEWS,
sources: [],
};
}
}
/** Singleton instance for use by the strategy registry */
export const breakingNewsStrategy = new BreakingNewsStrategy();
|