feat: Telegram OSINT map layer, Osiris intel ports, and maritime settings

Add Telegram OSINT with hourly incremental t.me scraping, metro geocoding
separate from news centroids, threat-intercept popup UI with inline media,
and HTML markers above alert boxes so pins stay clickable. Expose GFW_API_TOKEN
in onboarding and Settings Maritime; harden GFW/CCTV/geo fetchers. Port Osiris-
derived recon, SCM, entity graph, malware/cyber feeds, sanctions, and submarine
cable layers with tests and documentation.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
BigBodyCobain
2026-06-08 21:04:08 -06:00
parent b64b9e0962
commit af9b3d08cc
76 changed files with 5769 additions and 218 deletions
+182 -1
View File
@@ -165,8 +165,12 @@ export function buildEarthquakesGeoJSON(earthquakes?: Earthquake[]): FC {
properties: {
id: i,
type: 'earthquake',
name: `[M${eq.mag}]\n${eq.place || 'Unknown Location'}`,
name: `[M${eq.mag}] ${eq.place || 'Unknown Location'}`,
title: eq.title,
lat: eq.lat,
lng: eq.lng,
mag: eq.mag,
place: eq.place,
},
geometry: { type: 'Point' as const, coordinates: [eq.lng, eq.lat] },
};
@@ -1566,6 +1570,183 @@ export function buildCrowdThreatGeoJSON(threats?: CrowdThreatItem[], inView?: In
};
}
// ─── Telegram OSINT ───────────────────────────────────────────────────────
/** Group geoparsed posts by city-level coordinates (~1 km grid). */
export function telegramClusterKey(lat: number, lng: number): string {
return `${lat.toFixed(2)}_${lng.toFixed(2)}`;
}
/** Small fixed shift (~5 mi NE) only when a threat alert shares the same city grid. */
export const TELEGRAM_ALERT_AVOID_METERS = 8_000;
export const TELEGRAM_ALERT_AVOID_BEARING = 45;
/** HTML marker nudge — threat alerts are DOM overlays that cover map canvas dots. */
export const TELEGRAM_MARKER_OFFSET: [number, number] = [28, -24];
export function telegramClusterNearNewsAlert(
lat: number,
lng: number,
news?: Array<{ coords?: [number, number] | null }> | null,
): boolean {
if (!news?.length) return false;
const key = telegramClusterKey(lat, lng);
return news.some((item) => {
const coords = item.coords;
if (!coords || coords.length < 2) return false;
return telegramClusterKey(coords[0], coords[1]) === key;
});
}
export function telegramMapPinCoords(
lat: number,
lng: number,
avoidAlert: boolean,
): [number, number] {
if (!avoidAlert) return [lat, lng];
return projectPoint(lat, lng, TELEGRAM_ALERT_AVOID_BEARING, TELEGRAM_ALERT_AVOID_METERS);
}
export function applyTelegramAlertAvoidance(
geo: FC,
news?: Array<{ coords?: [number, number] | null }> | null,
): FC {
if (!geo?.features?.length) return geo;
return {
...geo,
features: geo.features.map((feature) => {
const geometry = feature.geometry;
if (!geometry || geometry.type !== 'Point') return feature;
const point = geometry.coordinates;
if (!point || point.length < 2) return feature;
const lng = point[0];
const lat = point[1];
const avoid = telegramClusterNearNewsAlert(lat, lng, news);
if (!avoid) return feature;
const [pinLat, pinLng] = telegramMapPinCoords(lat, lng, true);
return {
...feature,
geometry: {
type: 'Point' as const,
coordinates: [pinLng, pinLat],
},
};
}),
};
}
export function buildTelegramOsintGeoJSON(
payload?: {
posts?: Array<{
id: string;
title?: string;
description?: string;
link?: string;
source?: string;
channel?: string;
risk_score?: number;
coords?: [number, number] | null;
}>;
},
inView?: InViewFilter,
): FC {
const posts = payload?.posts;
if (!posts?.length) return null;
const clusters = new Map<
string,
{
lat: number;
lng: number;
posts: NonNullable<typeof posts>;
maxRisk: number;
}
>();
for (const post of posts) {
const coords = post.coords;
if (!coords || coords.length < 2) continue;
const lat = coords[0];
const lng = coords[1];
if (inView && !inView(lat, lng)) continue;
const key = telegramClusterKey(lat, lng);
const bucket = clusters.get(key);
if (bucket) {
bucket.posts.push(post);
bucket.maxRisk = Math.max(bucket.maxRisk, post.risk_score ?? 1);
} else {
clusters.set(key, {
lat,
lng,
posts: [post],
maxRisk: post.risk_score ?? 1,
});
}
}
if (!clusters.size) return null;
return {
type: 'FeatureCollection' as const,
features: Array.from(clusters.entries()).map(([key, cluster]) => {
const lead = cluster.posts[0];
const count = cluster.posts.length;
return {
type: 'Feature' as const,
properties: {
id: key,
type: 'telegram_osint',
name:
count > 1
? `Telegram OSINT (${count} posts)`
: lead.title || 'Telegram OSINT',
description: lead.description || '',
link: lead.link || '',
source: lead.source || '',
channel: lead.channel || '',
risk_score: cluster.maxRisk,
post_count: count,
},
geometry: {
type: 'Point' as const,
coordinates: [cluster.lng, cluster.lat],
},
};
}),
};
}
// ─── Malware C2 / URLhaus ─────────────────────────────────────────────────
export function buildMalwareGeoJSON(
payload?: { threats?: Array<{ id: string; lat: number; lng: number; ip: string; malware: string; threat_type?: string; country?: string }> },
inView?: InViewFilter,
): FC {
const threats = payload?.threats;
if (!threats?.length) return null;
return {
type: 'FeatureCollection' as const,
features: threats
.map((t) => {
if (t.lat == null || t.lng == null) return null;
if (inView && !inView(t.lat, t.lng)) return null;
return {
type: 'Feature' as const,
properties: {
id: t.id,
type: 'malware',
name: t.malware,
ip: t.ip,
threat_type: t.threat_type || 'malware',
country: t.country || '',
},
geometry: { type: 'Point' as const, coordinates: [t.lng, t.lat] },
};
})
.filter(Boolean) as GeoJSON.Feature[],
};
}
// ─── Wastewater colors by alert level ────────────────────────────────────
const WW_COLORS = {
alert: '#ff3333', // red — elevated pathogen detected