import { NextResponse } from 'next/server'; import crypto from 'crypto'; /** * OSIRIS — Military-Grade Intelligence API * Fetches Telegram OSINT feeds directly, with a failsafe fallback * to traditional intelligence sources if Telegram blocks the IP. */ const TELEGRAM_CHANNELS = [ 'OSINTtechnical', 'Faytuks', 'Liveuamap', 'CyberKnow' ]; const FALLBACK_FEEDS = { BBC: 'https://feeds.bbci.co.uk/news/world/rss.xml', AlJazeera: 'https://www.aljazeera.com/xml/rss/all.xml', GDACS: 'https://www.gdacs.org/xml/rss.xml' }; const RISK_KEYWORDS = ['war','missile','strike','attack','crisis','tension','military','conflict','defense','clash','nuclear','invasion','bomb','drone','weapon','sanctions','ceasefire','escalation', 'killed', 'destroyed', 'operation', 'casualty', 'frontline', 'threat']; const KEYWORD_COORDS: Record = { 'ukraine': [49.487, 31.272], 'kyiv': [50.450, 30.523], 'russia': [61.524, 105.318], 'moscow': [55.755, 37.617], 'israel': [31.046, 34.851], 'gaza': [31.416, 34.333], 'iran': [32.427, 53.688], 'lebanon': [33.854, 35.862], 'syria': [34.802, 38.996], 'yemen': [15.552, 48.516], 'china': [35.861, 104.195], 'taiwan': [23.697, 120.960], 'united states': [38.907, -77.036], 'europe': [48.800, 2.300], 'middle east': [31.500, 34.800] }; function scoreRisk(text: string): number { const lower = text.toLowerCase(); let score = 1; for (const kw of RISK_KEYWORDS) { if (lower.includes(kw)) score += 2; } return Math.min(10, score); } function findCoords(text: string): [number, number] | null { const lower = text.toLowerCase(); for (const [keyword, coords] of Object.entries(KEYWORD_COORDS)) { if (lower.includes(keyword)) return coords; } return null; } function parseTelegramHTML(html: string, channel: string): any[] { const items: any[] = []; const messageBlockRegex = /
\s*<\/div>\s*<\/div>/gi; let blockMatch; while ((blockMatch = messageBlockRegex.exec(html)) !== null) { const blockHtml = blockMatch[0]; const textRegex = /
([\s\S]*?)<\/item>/gi; let match; while ((match = itemRegex.exec(xml)) !== null) { const itemXml = match[1]; const getTag = (tag: string) => { const m = itemXml.match(new RegExp(`<${tag}[^>]*><\\/${tag}>|<${tag}[^>]*>([\\s\\S]*?)<\\/${tag}>`, 'i')); return (m?.[1] || m?.[2] || '').trim(); }; const title = getTag('title').replace(/<[^>]+>/g, ''); const desc = getTag('description').replace(/<[^>]+>/g, '').replace(/"/g, '"'); items.push({ title: title.length > 100 ? title.substring(0, 100) + '...' : title, description: desc, link: getTag('link'), pubDate: getTag('pubDate') || new Date().toISOString(), source: sourceName }); } return items; } export async function GET() { try { const feedPromises = TELEGRAM_CHANNELS.map(async (channel) => { try { const res = await fetch(`https://t.me/s/${channel}`, { signal: AbortSignal.timeout(8000), headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' } }); if (!res.ok) return []; const html = await res.text(); return parseTelegramHTML(html, channel).slice(-8); } catch { return []; } }); const feedResults = await Promise.allSettled(feedPromises); const allArticles: any[] = []; for (const result of feedResults) { if (result.status === 'fulfilled') allArticles.push(...result.value); } // FAILSAFE: If Telegram completely blocks the IP, fall back to traditional RSS if (allArticles.length === 0) { const fallbackPromises = Object.entries(FALLBACK_FEEDS).map(async ([source, url]) => { try { const res = await fetch(url, { signal: AbortSignal.timeout(5000) }); if (!res.ok) return []; const xml = await res.text(); return parseRSSItems(xml, source).slice(0, 5); } catch { return []; } }); const fallbackResults = await Promise.allSettled(fallbackPromises); for (const result of fallbackResults) { if (result.status === 'fulfilled') allArticles.push(...result.value); } } const newsItems = allArticles.map(article => { const riskScore = scoreRisk(article.description || article.title); const coords = findCoords(article.description || article.title); return { id: crypto.createHash('md5').update((article.link || '') + (article.pubDate || '')).digest('hex'), title: article.title, description: article.description, link: article.link, published: article.pubDate, source: article.source, risk_score: riskScore, coords: coords ? [coords[0], coords[1]] : null, coords_default: !coords, machine_assessment: riskScore >= 8 ? "AI Analysis indicates elevated tactical priority based on OSINT stream patterns." : null, }; }); newsItems.sort((a, b) => new Date(b.published).getTime() - new Date(a.published).getTime()); return NextResponse.json({ news: newsItems, total: newsItems.length, timestamp: new Date().toISOString(), }, { headers: { 'Cache-Control': 'public, s-maxage=60, stale-while-revalidate=120', }, }); } catch (error) { return NextResponse.json({ news: [], error: 'Failed to fetch intel' }, { status: 500 }); } }