@cyanheads/gdelt-mcp-server 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +405 -0
- package/Dockerfile +98 -0
- package/README.md +336 -0
- package/changelog/0.1.x/0.1.0.md +23 -0
- package/changelog/0.1.x/0.1.1.md +18 -0
- package/changelog/template.md +119 -0
- package/dist/config/server-config.d.ts +13 -0
- package/dist/config/server-config.d.ts.map +1 -0
- package/dist/config/server-config.js +28 -0
- package/dist/config/server-config.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +44 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-server/tools/date-resolution.d.ts +7 -0
- package/dist/mcp-server/tools/date-resolution.d.ts.map +1 -0
- package/dist/mcp-server/tools/date-resolution.js +13 -0
- package/dist/mcp-server/tools/date-resolution.js.map +1 -0
- package/dist/mcp-server/tools/definitions/get-coverage-breakdown.tool.d.ts +52 -0
- package/dist/mcp-server/tools/definitions/get-coverage-breakdown.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/get-coverage-breakdown.tool.js +182 -0
- package/dist/mcp-server/tools/definitions/get-coverage-breakdown.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/get-coverage-timeline.tool.d.ts +54 -0
- package/dist/mcp-server/tools/definitions/get-coverage-timeline.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/get-coverage-timeline.tool.js +173 -0
- package/dist/mcp-server/tools/definitions/get-coverage-timeline.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/get-tone-distribution.tool.d.ts +41 -0
- package/dist/mcp-server/tools/definitions/get-tone-distribution.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/get-tone-distribution.tool.js +143 -0
- package/dist/mcp-server/tools/definitions/get-tone-distribution.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/get-tv-clips.tool.d.ts +44 -0
- package/dist/mcp-server/tools/definitions/get-tv-clips.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/get-tv-clips.tool.js +134 -0
- package/dist/mcp-server/tools/definitions/get-tv-clips.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/get-tv-context.tool.d.ts +32 -0
- package/dist/mcp-server/tools/definitions/get-tv-context.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/get-tv-context.tool.js +114 -0
- package/dist/mcp-server/tools/definitions/get-tv-context.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/get-tv-trending.tool.d.ts +27 -0
- package/dist/mcp-server/tools/definitions/get-tv-trending.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/get-tv-trending.tool.js +82 -0
- package/dist/mcp-server/tools/definitions/get-tv-trending.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/index.d.ts +14 -0
- package/dist/mcp-server/tools/definitions/index.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/index.js +14 -0
- package/dist/mcp-server/tools/definitions/index.js.map +1 -0
- package/dist/mcp-server/tools/definitions/list-tv-stations.tool.d.ts +32 -0
- package/dist/mcp-server/tools/definitions/list-tv-stations.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/list-tv-stations.tool.js +94 -0
- package/dist/mcp-server/tools/definitions/list-tv-stations.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/search-articles.tool.d.ts +45 -0
- package/dist/mcp-server/tools/definitions/search-articles.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/search-articles.tool.js +142 -0
- package/dist/mcp-server/tools/definitions/search-articles.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/search-tv.tool.d.ts +48 -0
- package/dist/mcp-server/tools/definitions/search-tv.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/search-tv.tool.js +159 -0
- package/dist/mcp-server/tools/definitions/search-tv.tool.js.map +1 -0
- package/dist/services/gdelt/gdelt-doc-service.d.ts +89 -0
- package/dist/services/gdelt/gdelt-doc-service.d.ts.map +1 -0
- package/dist/services/gdelt/gdelt-doc-service.js +116 -0
- package/dist/services/gdelt/gdelt-doc-service.js.map +1 -0
- package/dist/services/gdelt/gdelt-fetch.d.ts +11 -0
- package/dist/services/gdelt/gdelt-fetch.d.ts.map +1 -0
- package/dist/services/gdelt/gdelt-fetch.js +46 -0
- package/dist/services/gdelt/gdelt-fetch.js.map +1 -0
- package/dist/services/gdelt/gdelt-tv-service.d.ts +78 -0
- package/dist/services/gdelt/gdelt-tv-service.d.ts.map +1 -0
- package/dist/services/gdelt/gdelt-tv-service.js +153 -0
- package/dist/services/gdelt/gdelt-tv-service.js.map +1 -0
- package/dist/services/gdelt/rate-limiter.d.ts +19 -0
- package/dist/services/gdelt/rate-limiter.d.ts.map +1 -0
- package/dist/services/gdelt/rate-limiter.js +47 -0
- package/dist/services/gdelt/rate-limiter.js.map +1 -0
- package/dist/services/gdelt/types.d.ts +87 -0
- package/dist/services/gdelt/types.d.ts.map +1 -0
- package/dist/services/gdelt/types.js +6 -0
- package/dist/services/gdelt/types.js.map +1 -0
- package/package.json +80 -0
- package/server.json +99 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Service wrapping the GDELT DOC API v2. Handles article search,
|
|
3
|
+
* coverage timelines, tone distribution, and language/country breakdowns.
|
|
4
|
+
* @module services/gdelt/gdelt-doc-service
|
|
5
|
+
*/
|
|
6
|
+
import type { Context } from '@cyanheads/mcp-ts-core';
|
|
7
|
+
import type { AppConfig } from '@cyanheads/mcp-ts-core/config';
|
|
8
|
+
import type { StorageService } from '@cyanheads/mcp-ts-core/storage';
|
|
9
|
+
import type { ServerConfig } from '../../config/server-config.js';
|
|
10
|
+
import type { Article } from './types.js';
|
|
11
|
+
export type DocTimelineMode = 'timelinevol' | 'timelinevolinfo' | 'timelinetone';
|
|
12
|
+
export type DocBreakdownMode = 'timelinelang' | 'timelinesourcecountry';
|
|
13
|
+
export type DocSearchParams = {
|
|
14
|
+
query: string;
|
|
15
|
+
timespan?: string;
|
|
16
|
+
startDatetime?: string;
|
|
17
|
+
endDatetime?: string;
|
|
18
|
+
maxRecords?: number;
|
|
19
|
+
sort?: 'date' | 'relevance' | 'social';
|
|
20
|
+
};
|
|
21
|
+
export type DocTimelineParams = {
|
|
22
|
+
query: string;
|
|
23
|
+
mode: DocTimelineMode;
|
|
24
|
+
timespan?: string;
|
|
25
|
+
startDatetime?: string;
|
|
26
|
+
endDatetime?: string;
|
|
27
|
+
smoothing?: number;
|
|
28
|
+
};
|
|
29
|
+
export type DocToneParams = {
|
|
30
|
+
query: string;
|
|
31
|
+
timespan?: string;
|
|
32
|
+
startDatetime?: string;
|
|
33
|
+
endDatetime?: string;
|
|
34
|
+
};
|
|
35
|
+
export type DocBreakdownParams = {
|
|
36
|
+
query: string;
|
|
37
|
+
mode: DocBreakdownMode;
|
|
38
|
+
timespan?: string;
|
|
39
|
+
startDatetime?: string;
|
|
40
|
+
endDatetime?: string;
|
|
41
|
+
};
|
|
42
|
+
export type ArticleSearchResult = {
|
|
43
|
+
articles: Article[];
|
|
44
|
+
totalReturned: number;
|
|
45
|
+
};
|
|
46
|
+
export type TimelineDataPoint = {
|
|
47
|
+
date: string;
|
|
48
|
+
value: number;
|
|
49
|
+
articles?: Array<{
|
|
50
|
+
url: string;
|
|
51
|
+
title: string;
|
|
52
|
+
}>;
|
|
53
|
+
};
|
|
54
|
+
export type TimelineSeries = {
|
|
55
|
+
label: string;
|
|
56
|
+
data: TimelineDataPoint[];
|
|
57
|
+
};
|
|
58
|
+
export type ToneHistogramBin = {
|
|
59
|
+
bin: number;
|
|
60
|
+
count: number;
|
|
61
|
+
articles: Array<{
|
|
62
|
+
url: string;
|
|
63
|
+
title: string;
|
|
64
|
+
}>;
|
|
65
|
+
};
|
|
66
|
+
export type BreakdownSeries = {
|
|
67
|
+
label: string;
|
|
68
|
+
data: Array<{
|
|
69
|
+
date: string;
|
|
70
|
+
value: number;
|
|
71
|
+
}>;
|
|
72
|
+
};
|
|
73
|
+
export declare class GdeltDocService {
|
|
74
|
+
private readonly baseUrl;
|
|
75
|
+
constructor(_config: AppConfig, _storage: StorageService, serverConfig: ServerConfig);
|
|
76
|
+
/** Search articles (artlist mode). */
|
|
77
|
+
searchArticles(params: DocSearchParams, ctx: Context): Promise<ArticleSearchResult>;
|
|
78
|
+
/** Fetch a coverage or tone timeline. */
|
|
79
|
+
getTimeline(params: DocTimelineParams, ctx: Context): Promise<TimelineSeries[]>;
|
|
80
|
+
/** Fetch tone distribution histogram (tonechart mode). */
|
|
81
|
+
getToneDistribution(params: DocToneParams, ctx: Context): Promise<ToneHistogramBin[]>;
|
|
82
|
+
/** Fetch language or source-country breakdown time series. */
|
|
83
|
+
getBreakdown(params: DocBreakdownParams, ctx: Context): Promise<BreakdownSeries[]>;
|
|
84
|
+
private buildBaseParams;
|
|
85
|
+
private fetch;
|
|
86
|
+
}
|
|
87
|
+
export declare function initGdeltDocService(config: AppConfig, storage: StorageService, serverConfig: ServerConfig): void;
|
|
88
|
+
export declare function getGdeltDocService(): GdeltDocService;
|
|
89
|
+
//# sourceMappingURL=gdelt-doc-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gdelt-doc-service.d.ts","sourceRoot":"","sources":["../../../src/services/gdelt/gdelt-doc-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Service wrapping the GDELT DOC API v2. Handles article search,
|
|
3
|
+
* coverage timelines, tone distribution, and language/country breakdowns.
|
|
4
|
+
* @module services/gdelt/gdelt-doc-service
|
|
5
|
+
*/
|
|
6
|
+
import { applyTimeRange, gdeltFetch } from './gdelt-fetch.js';
|
|
7
|
+
const DOC_ENDPOINT = '/doc/doc';
|
|
8
|
+
export class GdeltDocService {
|
|
9
|
+
baseUrl;
|
|
10
|
+
constructor(_config, _storage, serverConfig) {
|
|
11
|
+
this.baseUrl = serverConfig.baseUrl + DOC_ENDPOINT;
|
|
12
|
+
}
|
|
13
|
+
/** Search articles (artlist mode). */
|
|
14
|
+
async searchArticles(params, ctx) {
|
|
15
|
+
const urlParams = this.buildBaseParams(params.query);
|
|
16
|
+
urlParams.set('mode', 'artlist');
|
|
17
|
+
if (params.maxRecords)
|
|
18
|
+
urlParams.set('maxrecords', String(params.maxRecords));
|
|
19
|
+
if (params.sort)
|
|
20
|
+
urlParams.set('sort', params.sort);
|
|
21
|
+
applyTimeRange(urlParams, params.timespan, params.startDatetime, params.endDatetime);
|
|
22
|
+
const raw = await this.fetch(urlParams, ctx);
|
|
23
|
+
const articles = (raw.articles ?? []).map(normalizeArticle);
|
|
24
|
+
return { articles, totalReturned: articles.length };
|
|
25
|
+
}
|
|
26
|
+
/** Fetch a coverage or tone timeline. */
|
|
27
|
+
async getTimeline(params, ctx) {
|
|
28
|
+
const urlParams = this.buildBaseParams(params.query);
|
|
29
|
+
urlParams.set('mode', params.mode);
|
|
30
|
+
if (params.smoothing != null)
|
|
31
|
+
urlParams.set('smoothing', String(params.smoothing));
|
|
32
|
+
applyTimeRange(urlParams, params.timespan, params.startDatetime, params.endDatetime);
|
|
33
|
+
if (params.mode === 'timelinevolinfo') {
|
|
34
|
+
const raw = await this.fetch(urlParams, ctx);
|
|
35
|
+
return parseVolInfoTimeline(raw);
|
|
36
|
+
}
|
|
37
|
+
const raw = await this.fetch(urlParams, ctx);
|
|
38
|
+
return parseTimeline(raw);
|
|
39
|
+
}
|
|
40
|
+
/** Fetch tone distribution histogram (tonechart mode). */
|
|
41
|
+
async getToneDistribution(params, ctx) {
|
|
42
|
+
const urlParams = this.buildBaseParams(params.query);
|
|
43
|
+
urlParams.set('mode', 'tonechart');
|
|
44
|
+
applyTimeRange(urlParams, params.timespan, params.startDatetime, params.endDatetime);
|
|
45
|
+
const raw = await this.fetch(urlParams, ctx);
|
|
46
|
+
return (raw.tonechart ?? []).map((b) => ({
|
|
47
|
+
bin: b.bin,
|
|
48
|
+
count: b.count,
|
|
49
|
+
articles: b.toparts ?? [],
|
|
50
|
+
}));
|
|
51
|
+
}
|
|
52
|
+
/** Fetch language or source-country breakdown time series. */
|
|
53
|
+
async getBreakdown(params, ctx) {
|
|
54
|
+
const urlParams = this.buildBaseParams(params.query);
|
|
55
|
+
urlParams.set('mode', params.mode);
|
|
56
|
+
applyTimeRange(urlParams, params.timespan, params.startDatetime, params.endDatetime);
|
|
57
|
+
const raw = await this.fetch(urlParams, ctx);
|
|
58
|
+
return parseTimeline(raw).map((s) => ({
|
|
59
|
+
label: s.label,
|
|
60
|
+
data: s.data.map((d) => ({ date: d.date, value: d.value })),
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
// ─── Private helpers ────────────────────────────────────────────────────────
|
|
64
|
+
buildBaseParams(query) {
|
|
65
|
+
const p = new URLSearchParams();
|
|
66
|
+
p.set('query', query);
|
|
67
|
+
p.set('format', 'json');
|
|
68
|
+
return p;
|
|
69
|
+
}
|
|
70
|
+
fetch(params, ctx) {
|
|
71
|
+
return gdeltFetch(this.baseUrl, params, ctx, 'GdeltDocService.fetch', 'GDELT DOC');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// ─── Normalizers ─────────────────────────────────────────────────────────────
|
|
75
|
+
function normalizeArticle(raw) {
|
|
76
|
+
return {
|
|
77
|
+
url: raw.url,
|
|
78
|
+
title: raw.title,
|
|
79
|
+
seendate: raw.seendate,
|
|
80
|
+
domain: raw.domain,
|
|
81
|
+
language: raw.language,
|
|
82
|
+
sourcecountry: raw.sourcecountry,
|
|
83
|
+
...(raw.socialimage ? { socialimage: raw.socialimage } : {}),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function parseTimeline(raw) {
|
|
87
|
+
return (raw.timeline ?? []).map((s) => ({
|
|
88
|
+
label: s.series ?? 'Series',
|
|
89
|
+
data: (s.data ?? []).map((d) => ({ date: d.date, value: d.value })),
|
|
90
|
+
}));
|
|
91
|
+
}
|
|
92
|
+
function parseVolInfoTimeline(raw) {
|
|
93
|
+
return (raw.timeline ?? []).map((s) => ({
|
|
94
|
+
label: s.series ?? 'Volume Intensity',
|
|
95
|
+
data: (s.data ?? []).map((d) => ({
|
|
96
|
+
date: d.date,
|
|
97
|
+
value: d.value,
|
|
98
|
+
...(d.toparts?.length
|
|
99
|
+
? {
|
|
100
|
+
articles: d.toparts.map((a) => ({ url: a.url, title: a.title })),
|
|
101
|
+
}
|
|
102
|
+
: {}),
|
|
103
|
+
})),
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
106
|
+
// ─── Init/accessor pattern ────────────────────────────────────────────────────
|
|
107
|
+
let _service;
|
|
108
|
+
export function initGdeltDocService(config, storage, serverConfig) {
|
|
109
|
+
_service = new GdeltDocService(config, storage, serverConfig);
|
|
110
|
+
}
|
|
111
|
+
export function getGdeltDocService() {
|
|
112
|
+
if (!_service)
|
|
113
|
+
throw new Error('GdeltDocService not initialized — call initGdeltDocService() in setup()');
|
|
114
|
+
return _service;
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=gdelt-doc-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gdelt-doc-service.js","sourceRoot":"","sources":["../../../src/services/gdelt/gdelt-doc-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Shared fetch helper for GDELT API calls. Handles rate-limiting,
|
|
3
|
+
* retries, HTML error detection, and JSON parsing.
|
|
4
|
+
* @module services/gdelt/gdelt-fetch
|
|
5
|
+
*/
|
|
6
|
+
import type { Context } from '@cyanheads/mcp-ts-core';
|
|
7
|
+
/** Apply timespan or explicit date range to URL params. */
|
|
8
|
+
export declare function applyTimeRange(params: URLSearchParams, timespan?: string, startDatetime?: string, endDatetime?: string): void;
|
|
9
|
+
/** Fetch a GDELT endpoint with rate-limiting, retries, and JSON parsing. */
|
|
10
|
+
export declare function gdeltFetch<T>(baseUrl: string, params: URLSearchParams, ctx: Context, operation: string, apiLabel: string): Promise<T>;
|
|
11
|
+
//# sourceMappingURL=gdelt-fetch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gdelt-fetch.d.ts","sourceRoot":"","sources":["../../../src/services/gdelt/gdelt-fetch.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAKtD,2DAA2D;AAC3D,wBAAgB,cAAc,CAC5B,MAAM,EAAE,eAAe,EACvB,QAAQ,CAAC,EAAE,MAAM,EACjB,aAAa,CAAC,EAAE,MAAM,EACtB,WAAW,CAAC,EAAE,MAAM,GACnB,IAAI,CAON;AAED,4EAA4E;AAC5E,wBAAgB,UAAU,CAAC,CAAC,EAC1B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,eAAe,EACvB,GAAG,EAAE,OAAO,EACZ,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,CAAC,CAAC,CAcZ"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Shared fetch helper for GDELT API calls. Handles rate-limiting,
|
|
3
|
+
* retries, HTML error detection, and JSON parsing.
|
|
4
|
+
* @module services/gdelt/gdelt-fetch
|
|
5
|
+
*/
|
|
6
|
+
import { serializationError, serviceUnavailable } from '@cyanheads/mcp-ts-core/errors';
|
|
7
|
+
import { fetchWithTimeout, withRetry } from '@cyanheads/mcp-ts-core/utils';
|
|
8
|
+
import { getRateLimiter } from './rate-limiter.js';
|
|
9
|
+
/** Apply timespan or explicit date range to URL params. */
|
|
10
|
+
export function applyTimeRange(params, timespan, startDatetime, endDatetime) {
|
|
11
|
+
if (startDatetime && endDatetime) {
|
|
12
|
+
params.set('startdatetime', startDatetime);
|
|
13
|
+
params.set('enddatetime', endDatetime);
|
|
14
|
+
}
|
|
15
|
+
else if (timespan) {
|
|
16
|
+
params.set('timespan', timespan);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** Fetch a GDELT endpoint with rate-limiting, retries, and JSON parsing. */
|
|
20
|
+
export function gdeltFetch(baseUrl, params, ctx, operation, apiLabel) {
|
|
21
|
+
const limiter = getRateLimiter();
|
|
22
|
+
const rctx = ctx;
|
|
23
|
+
return withRetry(async () => {
|
|
24
|
+
await limiter.acquire();
|
|
25
|
+
const url = `${baseUrl}?${params.toString()}`;
|
|
26
|
+
ctx.log.debug(`${apiLabel} API request`, { url });
|
|
27
|
+
const response = await fetchWithTimeout(url, 30_000, rctx, { signal: ctx.signal });
|
|
28
|
+
const text = await response.text();
|
|
29
|
+
return parseGdeltJson(text, apiLabel);
|
|
30
|
+
}, { operation, context: rctx, baseDelayMs: 5100, signal: ctx.signal });
|
|
31
|
+
}
|
|
32
|
+
function parseGdeltJson(text, apiLabel) {
|
|
33
|
+
if (/^\s*<(!DOCTYPE\s+html|html[\s>])/i.test(text)) {
|
|
34
|
+
throw serviceUnavailable(`${apiLabel} API returned HTML — likely rate-limited or unavailable. Retry after 5 seconds.`);
|
|
35
|
+
}
|
|
36
|
+
if (text.trim().length === 0) {
|
|
37
|
+
throw serviceUnavailable(`${apiLabel} API returned an empty response — endpoint may be temporarily unavailable.`);
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
return JSON.parse(text);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
throw serializationError(`${apiLabel} API returned unparseable response: ${text.slice(0, 200)}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=gdelt-fetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gdelt-fetch.js","sourceRoot":"","sources":["../../../src/services/gdelt/gdelt-fetch.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,gBAAgB,EAA2B,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACpG,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD,2DAA2D;AAC3D,MAAM,UAAU,cAAc,CAC5B,MAAuB,EACvB,QAAiB,EACjB,aAAsB,EACtB,WAAoB;IAEpB,IAAI,aAAa,IAAI,WAAW,EAAE,CAAC;QACjC,MAAM,CAAC,GAAG,CAAC,eAAe,EAAE,aAAa,CAAC,CAAC;QAC3C,MAAM,CAAC,GAAG,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC;IACzC,CAAC;SAAM,IAAI,QAAQ,EAAE,CAAC;QACpB,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IACnC,CAAC;AACH,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,UAAU,CACxB,OAAe,EACf,MAAuB,EACvB,GAAY,EACZ,SAAiB,EACjB,QAAgB;IAEhB,MAAM,OAAO,GAAG,cAAc,EAAE,CAAC;IACjC,MAAM,IAAI,GAAG,GAAoC,CAAC;IAClD,OAAO,SAAS,CACd,KAAK,IAAI,EAAE;QACT,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;QACxB,MAAM,GAAG,GAAG,GAAG,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;QAC9C,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,QAAQ,cAAc,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;QAClD,MAAM,QAAQ,GAAG,MAAM,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QACnF,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnC,OAAO,cAAc,CAAI,IAAI,EAAE,QAAQ,CAAC,CAAC;IAC3C,CAAC,EACD,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CACpE,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAI,IAAY,EAAE,QAAgB;IACvD,IAAI,mCAAmC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,MAAM,kBAAkB,CACtB,GAAG,QAAQ,iFAAiF,CAC7F,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,MAAM,kBAAkB,CACtB,GAAG,QAAQ,4EAA4E,CACxF,CAAC;IACJ,CAAC;IACD,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAM,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,kBAAkB,CACtB,GAAG,QAAQ,uCAAuC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CACvE,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Service wrapping the GDELT TV API v2. Handles TV news transcript search,
|
|
3
|
+
* clip retrieval, context/word cloud, trending topics, and station metadata.
|
|
4
|
+
* @module services/gdelt/gdelt-tv-service
|
|
5
|
+
*/
|
|
6
|
+
import type { Context } from '@cyanheads/mcp-ts-core';
|
|
7
|
+
import type { AppConfig } from '@cyanheads/mcp-ts-core/config';
|
|
8
|
+
import type { StorageService } from '@cyanheads/mcp-ts-core/storage';
|
|
9
|
+
import type { ServerConfig } from '../../config/server-config.js';
|
|
10
|
+
import type { TvClip, TvStation } from './types.js';
|
|
11
|
+
export type TvSearchParams = {
|
|
12
|
+
query: string;
|
|
13
|
+
stations?: string[];
|
|
14
|
+
timespan?: string;
|
|
15
|
+
startDatetime?: string;
|
|
16
|
+
endDatetime?: string;
|
|
17
|
+
smoothing?: number;
|
|
18
|
+
normalize?: boolean;
|
|
19
|
+
};
|
|
20
|
+
export type TvClipParams = {
|
|
21
|
+
query: string;
|
|
22
|
+
stations?: string[];
|
|
23
|
+
timespan?: string;
|
|
24
|
+
startDatetime?: string;
|
|
25
|
+
endDatetime?: string;
|
|
26
|
+
maxRecords?: number;
|
|
27
|
+
sort?: 'relevance' | 'dateDesc' | 'dateAsc';
|
|
28
|
+
};
|
|
29
|
+
export type TvContextParams = {
|
|
30
|
+
query: string;
|
|
31
|
+
stations?: string[];
|
|
32
|
+
timespan?: string;
|
|
33
|
+
};
|
|
34
|
+
export type TvSearchSeries = {
|
|
35
|
+
station: string;
|
|
36
|
+
data: Array<{
|
|
37
|
+
date: string;
|
|
38
|
+
value: number;
|
|
39
|
+
}>;
|
|
40
|
+
};
|
|
41
|
+
export type TvContextWord = {
|
|
42
|
+
label: string;
|
|
43
|
+
score: number;
|
|
44
|
+
};
|
|
45
|
+
export type TvTrendingTopic = {
|
|
46
|
+
label: string;
|
|
47
|
+
score: number;
|
|
48
|
+
};
|
|
49
|
+
export declare class GdeltTvService {
|
|
50
|
+
private readonly baseUrl;
|
|
51
|
+
constructor(_config: AppConfig, _storage: StorageService, serverConfig: ServerConfig);
|
|
52
|
+
/** Search TV coverage — returns per-station normalized time series. */
|
|
53
|
+
searchTv(params: TvSearchParams, ctx: Context): Promise<{
|
|
54
|
+
series: TvSearchSeries[];
|
|
55
|
+
dateResolution: 'hour' | 'day' | 'month';
|
|
56
|
+
timeRange: {
|
|
57
|
+
start: string;
|
|
58
|
+
end: string;
|
|
59
|
+
};
|
|
60
|
+
normalized: boolean;
|
|
61
|
+
}>;
|
|
62
|
+
/** Retrieve TV clips (clip gallery). */
|
|
63
|
+
getTvClips(params: TvClipParams, ctx: Context): Promise<TvClip[]>;
|
|
64
|
+
/** Fetch word cloud / co-occurring terms for TV clips matching a query. */
|
|
65
|
+
getTvContext(params: TvContextParams, ctx: Context): Promise<{
|
|
66
|
+
words: TvContextWord[];
|
|
67
|
+
clipsAnalyzed: number;
|
|
68
|
+
}>;
|
|
69
|
+
/** Fetch currently trending topics on TV news. */
|
|
70
|
+
getTvTrending(ctx: Context): Promise<TvTrendingTopic[]>;
|
|
71
|
+
/** List all TV stations with metadata. */
|
|
72
|
+
listStations(ctx: Context): Promise<TvStation[]>;
|
|
73
|
+
private buildBaseParams;
|
|
74
|
+
private fetch;
|
|
75
|
+
}
|
|
76
|
+
export declare function initGdeltTvService(config: AppConfig, storage: StorageService, serverConfig: ServerConfig): void;
|
|
77
|
+
export declare function getGdeltTvService(): GdeltTvService;
|
|
78
|
+
//# sourceMappingURL=gdelt-tv-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gdelt-tv-service.d.ts","sourceRoot":"","sources":["../../../src/services/gdelt/gdelt-tv-service.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAE9D,OAAO,KAAK,EAAsC,MAAM,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAOxF,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,WAAW,GAAG,UAAU,GAAG,SAAS,CAAC;CAC7C,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC9C,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY;IAIpF,uEAAuE;IACjE,QAAQ,CACZ,MAAM,EAAE,cAAc,EACtB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC;QACT,MAAM,EAAE,cAAc,EAAE,CAAC;QACzB,cAAc,EAAE,MAAM,GAAG,KAAK,GAAG,OAAO,CAAC;QACzC,SAAS,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAA;SAAE,CAAC;QAC1C,UAAU,EAAE,OAAO,CAAC;KACrB,CAAC;IA8BF,wCAAwC;IAClC,UAAU,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAsBvE,2EAA2E;IACrE,YAAY,CAChB,MAAM,EAAE,eAAe,EACvB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC;QACT,KAAK,EAAE,aAAa,EAAE,CAAC;QACvB,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;IAaF,kDAAkD;IAC5C,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAe7D,0CAA0C;IACpC,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAyBtD,OAAO,CAAC,eAAe;IAYvB,OAAO,CAAC,KAAK;CAGd;AA0BD,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,cAAc,EACvB,YAAY,EAAE,YAAY,GACzB,IAAI,CAEN;AAED,wBAAgB,iBAAiB,IAAI,cAAc,CAIlD"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Service wrapping the GDELT TV API v2. Handles TV news transcript search,
|
|
3
|
+
* clip retrieval, context/word cloud, trending topics, and station metadata.
|
|
4
|
+
* @module services/gdelt/gdelt-tv-service
|
|
5
|
+
*/
|
|
6
|
+
import { applyTimeRange, gdeltFetch } from './gdelt-fetch.js';
|
|
7
|
+
const TV_ENDPOINT = '/tv/tv';
|
|
8
|
+
/** Threshold for "active" station: endDate within this many milliseconds of now. */
|
|
9
|
+
const ACTIVE_THRESHOLD_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
10
|
+
export class GdeltTvService {
|
|
11
|
+
baseUrl;
|
|
12
|
+
constructor(_config, _storage, serverConfig) {
|
|
13
|
+
this.baseUrl = serverConfig.baseUrl + TV_ENDPOINT;
|
|
14
|
+
}
|
|
15
|
+
/** Search TV coverage — returns per-station normalized time series. */
|
|
16
|
+
async searchTv(params, ctx) {
|
|
17
|
+
const urlParams = this.buildBaseParams(params.query, params.stations);
|
|
18
|
+
urlParams.set('mode', params.normalize !== false ? 'timelinevolnorm' : 'timelinevol');
|
|
19
|
+
if (params.smoothing != null)
|
|
20
|
+
urlParams.set('smoothing', String(params.smoothing));
|
|
21
|
+
applyTimeRange(urlParams, params.timespan, params.startDatetime, params.endDatetime);
|
|
22
|
+
const raw = await this.fetch(urlParams, ctx);
|
|
23
|
+
const series = (raw.timeline ?? []).map((s) => ({
|
|
24
|
+
station: s.series ?? 'Unknown',
|
|
25
|
+
data: (s.data ?? []).map((d) => ({ date: d.date, value: d.value })),
|
|
26
|
+
}));
|
|
27
|
+
const allDates = series.flatMap((s) => s.data.map((d) => d.date));
|
|
28
|
+
const sorted = allDates.slice().sort();
|
|
29
|
+
const timeRange = {
|
|
30
|
+
start: sorted[0] ?? '',
|
|
31
|
+
end: sorted[sorted.length - 1] ?? '',
|
|
32
|
+
};
|
|
33
|
+
const resStr = raw.dateresolution ?? 'day';
|
|
34
|
+
const dateResolution = resStr === 'hour' ? 'hour' : resStr === 'month' ? 'month' : 'day';
|
|
35
|
+
return { series, dateResolution, timeRange, normalized: params.normalize !== false };
|
|
36
|
+
}
|
|
37
|
+
/** Retrieve TV clips (clip gallery). */
|
|
38
|
+
async getTvClips(params, ctx) {
|
|
39
|
+
const urlParams = this.buildBaseParams(params.query, params.stations);
|
|
40
|
+
urlParams.set('mode', 'clipgallery');
|
|
41
|
+
if (params.maxRecords)
|
|
42
|
+
urlParams.set('maxrecords', String(params.maxRecords));
|
|
43
|
+
if (params.sort) {
|
|
44
|
+
const sortMap = { relevance: 'relevance', dateDesc: 'date', dateAsc: 'date' };
|
|
45
|
+
urlParams.set('sort', sortMap[params.sort]);
|
|
46
|
+
if (params.sort === 'dateAsc')
|
|
47
|
+
urlParams.set('sortdir', 'asc');
|
|
48
|
+
}
|
|
49
|
+
applyTimeRange(urlParams, params.timespan, params.startDatetime, params.endDatetime);
|
|
50
|
+
const raw = await this.fetch(urlParams, ctx);
|
|
51
|
+
return (raw.clips ?? []).map((c) => ({
|
|
52
|
+
show: c.show,
|
|
53
|
+
station: c.station,
|
|
54
|
+
date: c.date,
|
|
55
|
+
snippet: c.snippet,
|
|
56
|
+
archiveUrl: c.preview_url,
|
|
57
|
+
...(c.preview_thumb ? { thumbnail: c.preview_thumb } : {}),
|
|
58
|
+
}));
|
|
59
|
+
}
|
|
60
|
+
/** Fetch word cloud / co-occurring terms for TV clips matching a query. */
|
|
61
|
+
async getTvContext(params, ctx) {
|
|
62
|
+
const urlParams = this.buildBaseParams(params.query, params.stations);
|
|
63
|
+
urlParams.set('mode', 'wordcloud');
|
|
64
|
+
if (params.timespan)
|
|
65
|
+
urlParams.set('timespan', params.timespan);
|
|
66
|
+
const raw = await this.fetch(urlParams, ctx);
|
|
67
|
+
const words = (raw.wordcloud ?? []).map((w) => ({
|
|
68
|
+
label: w.label,
|
|
69
|
+
score: w.count,
|
|
70
|
+
}));
|
|
71
|
+
return { words, clipsAnalyzed: raw.numclips ?? 0 };
|
|
72
|
+
}
|
|
73
|
+
/** Fetch currently trending topics on TV news. */
|
|
74
|
+
async getTvTrending(ctx) {
|
|
75
|
+
const urlParams = new URLSearchParams();
|
|
76
|
+
urlParams.set('mode', 'trendingtopics');
|
|
77
|
+
urlParams.set('format', 'json');
|
|
78
|
+
const raw = await this.fetch(urlParams, ctx);
|
|
79
|
+
return (raw.OverallTrendingTopics ?? raw.OverallTrendingPhrases ?? []).map((t) => ({
|
|
80
|
+
label: t.label,
|
|
81
|
+
score: t.score,
|
|
82
|
+
}));
|
|
83
|
+
}
|
|
84
|
+
/** List all TV stations with metadata. */
|
|
85
|
+
async listStations(ctx) {
|
|
86
|
+
const urlParams = new URLSearchParams();
|
|
87
|
+
urlParams.set('mode', 'stationdetails');
|
|
88
|
+
urlParams.set('format', 'json');
|
|
89
|
+
const raw = await this.fetch(urlParams, ctx);
|
|
90
|
+
const now = Date.now();
|
|
91
|
+
return (raw.station_details ?? []).map((s) => {
|
|
92
|
+
const endMs = parseGdeltDate(s.EndDate);
|
|
93
|
+
const isActive = endMs != null && now - endMs < ACTIVE_THRESHOLD_MS;
|
|
94
|
+
return {
|
|
95
|
+
stationId: s.StationID,
|
|
96
|
+
description: s.Description,
|
|
97
|
+
market: s.Market,
|
|
98
|
+
network: s.Network,
|
|
99
|
+
startDate: formatGdeltDate(s.StartDate),
|
|
100
|
+
endDate: formatGdeltDate(s.EndDate),
|
|
101
|
+
isActive,
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
// ─── Private helpers ────────────────────────────────────────────────────────
|
|
106
|
+
buildBaseParams(query, stations) {
|
|
107
|
+
const p = new URLSearchParams();
|
|
108
|
+
// TV API embeds station filters inside the query string
|
|
109
|
+
let q = query;
|
|
110
|
+
if (stations?.length) {
|
|
111
|
+
q += ' ' + stations.map((s) => `station:${s}`).join(' ');
|
|
112
|
+
}
|
|
113
|
+
p.set('query', q);
|
|
114
|
+
p.set('format', 'json');
|
|
115
|
+
return p;
|
|
116
|
+
}
|
|
117
|
+
fetch(params, ctx) {
|
|
118
|
+
return gdeltFetch(this.baseUrl, params, ctx, 'GdeltTvService.fetch', 'GDELT TV');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/** Parse GDELT date string (YYYYMMDDHHMMSS or YYYYMMDD) to ms epoch. Returns undefined on failure. */
|
|
122
|
+
function parseGdeltDate(s) {
|
|
123
|
+
if (!s)
|
|
124
|
+
return;
|
|
125
|
+
const clean = s.replace(/\D/g, '');
|
|
126
|
+
if (clean.length < 8)
|
|
127
|
+
return;
|
|
128
|
+
const year = parseInt(clean.slice(0, 4), 10);
|
|
129
|
+
const month = parseInt(clean.slice(4, 6), 10) - 1;
|
|
130
|
+
const day = parseInt(clean.slice(6, 8), 10);
|
|
131
|
+
const d = new Date(year, month, day);
|
|
132
|
+
return Number.isNaN(d.getTime()) ? undefined : d.getTime();
|
|
133
|
+
}
|
|
134
|
+
/** Convert GDELT date string to ISO 8601. Returns original on failure. */
|
|
135
|
+
function formatGdeltDate(s) {
|
|
136
|
+
if (!s)
|
|
137
|
+
return s;
|
|
138
|
+
const clean = s.replace(/\D/g, '');
|
|
139
|
+
if (clean.length < 8)
|
|
140
|
+
return s;
|
|
141
|
+
return `${clean.slice(0, 4)}-${clean.slice(4, 6)}-${clean.slice(6, 8)}`;
|
|
142
|
+
}
|
|
143
|
+
// ─── Init/accessor pattern ────────────────────────────────────────────────────
|
|
144
|
+
let _service;
|
|
145
|
+
export function initGdeltTvService(config, storage, serverConfig) {
|
|
146
|
+
_service = new GdeltTvService(config, storage, serverConfig);
|
|
147
|
+
}
|
|
148
|
+
export function getGdeltTvService() {
|
|
149
|
+
if (!_service)
|
|
150
|
+
throw new Error('GdeltTvService not initialized — call initGdeltTvService() in setup()');
|
|
151
|
+
return _service;
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=gdelt-tv-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gdelt-tv-service.js","sourceRoot":"","sources":["../../../src/services/gdelt/gdelt-tv-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Shared rate limiter for GDELT API calls. Enforces 1 request per 5 seconds
|
|
3
|
+
* across all API calls (DOC and TV) via a serial queue with minimum delay.
|
|
4
|
+
* @module services/gdelt/rate-limiter
|
|
5
|
+
*/
|
|
6
|
+
/** Singleton that serializes all GDELT API calls with a minimum inter-request delay. */
|
|
7
|
+
export declare class GdeltRateLimiter {
|
|
8
|
+
private readonly delayMs;
|
|
9
|
+
private lastRequestTime;
|
|
10
|
+
private queue;
|
|
11
|
+
private processing;
|
|
12
|
+
constructor(delayMs: number);
|
|
13
|
+
/** Acquire a rate-limit slot. Resolves when it is safe to issue the next request. */
|
|
14
|
+
acquire(): Promise<void>;
|
|
15
|
+
private processQueue;
|
|
16
|
+
}
|
|
17
|
+
export declare function initRateLimiter(delayMs: number): void;
|
|
18
|
+
export declare function getRateLimiter(): GdeltRateLimiter;
|
|
19
|
+
//# sourceMappingURL=rate-limiter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../../src/services/gdelt/rate-limiter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,wFAAwF;AACxF,qBAAa,gBAAgB;IAKf,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJpC,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,KAAK,CAAyB;IACtC,OAAO,CAAC,UAAU,CAAS;gBAEE,OAAO,EAAE,MAAM;IAE5C,qFAAqF;IACrF,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;YAOV,YAAY;CAc3B;AAID,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAErD;AAED,wBAAgB,cAAc,IAAI,gBAAgB,CAIjD"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Shared rate limiter for GDELT API calls. Enforces 1 request per 5 seconds
|
|
3
|
+
* across all API calls (DOC and TV) via a serial queue with minimum delay.
|
|
4
|
+
* @module services/gdelt/rate-limiter
|
|
5
|
+
*/
|
|
6
|
+
/** Singleton that serializes all GDELT API calls with a minimum inter-request delay. */
|
|
7
|
+
export class GdeltRateLimiter {
|
|
8
|
+
delayMs;
|
|
9
|
+
lastRequestTime = 0;
|
|
10
|
+
queue = [];
|
|
11
|
+
processing = false;
|
|
12
|
+
constructor(delayMs) {
|
|
13
|
+
this.delayMs = delayMs;
|
|
14
|
+
}
|
|
15
|
+
/** Acquire a rate-limit slot. Resolves when it is safe to issue the next request. */
|
|
16
|
+
acquire() {
|
|
17
|
+
return new Promise((resolve) => {
|
|
18
|
+
this.queue.push(resolve);
|
|
19
|
+
if (!this.processing)
|
|
20
|
+
this.processQueue();
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
async processQueue() {
|
|
24
|
+
this.processing = true;
|
|
25
|
+
while (this.queue.length > 0) {
|
|
26
|
+
const now = Date.now();
|
|
27
|
+
const elapsed = now - this.lastRequestTime;
|
|
28
|
+
if (elapsed < this.delayMs) {
|
|
29
|
+
await new Promise((r) => setTimeout(r, this.delayMs - elapsed));
|
|
30
|
+
}
|
|
31
|
+
this.lastRequestTime = Date.now();
|
|
32
|
+
const next = this.queue.shift();
|
|
33
|
+
next?.();
|
|
34
|
+
}
|
|
35
|
+
this.processing = false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
let _limiter;
|
|
39
|
+
export function initRateLimiter(delayMs) {
|
|
40
|
+
_limiter = new GdeltRateLimiter(delayMs);
|
|
41
|
+
}
|
|
42
|
+
export function getRateLimiter() {
|
|
43
|
+
if (!_limiter)
|
|
44
|
+
throw new Error('GdeltRateLimiter not initialized — call initRateLimiter() in setup()');
|
|
45
|
+
return _limiter;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=rate-limiter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../../src/services/gdelt/rate-limiter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,wFAAwF;AACxF,MAAM,OAAO,gBAAgB;IAKE;IAJrB,eAAe,GAAG,CAAC,CAAC;IACpB,KAAK,GAAsB,EAAE,CAAC;IAC9B,UAAU,GAAG,KAAK,CAAC;IAE3B,YAA6B,OAAe;QAAf,YAAO,GAAP,OAAO,CAAQ;IAAG,CAAC;IAEhD,qFAAqF;IACrF,OAAO;QACL,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YACnC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACzB,IAAI,CAAC,IAAI,CAAC,UAAU;gBAAE,IAAI,CAAC,YAAY,EAAE,CAAC;QAC5C,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,YAAY;QACxB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACvB,MAAM,OAAO,GAAG,GAAG,GAAG,IAAI,CAAC,eAAe,CAAC;YAC3C,IAAI,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;gBAC3B,MAAM,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;YACxE,CAAC;YACD,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAClC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YAChC,IAAI,EAAE,EAAE,CAAC;QACX,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;IAC1B,CAAC;CACF;AAED,IAAI,QAAsC,CAAC;AAE3C,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,QAAQ,GAAG,IAAI,gBAAgB,CAAC,OAAO,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,IAAI,CAAC,QAAQ;QACX,MAAM,IAAI,KAAK,CAAC,sEAAsE,CAAC,CAAC;IAC1F,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|