Claude Skill

gdelt

Global multilingual news event stream with tone scoring via GDELT 2.0 Doc API.

LLM Mart · 0 points · 11 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download kansoku-trade-kansoku-.claude_skills_gdelt-4994657.zip · 3 KB
Part of kansoku-trade/kansoku — 11 skills

Install

skills CLI npx skills add https://github.com/kansoku-trade/kansoku/tree/main/.claude/skills/gdelt
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install kansoku-trade-kansoku@llmmart
Git git clone https://github.com/kansoku-trade/kansoku.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole kansoku-trade/kansoku collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

gdelt

Response language: match user input.

⚠️ GDELT is a rolling recent-window API, not a historical event archive. Time windows are anchored to absolute timestamps (UTC) for reproducibility — the same query asked tomorrow will return different results.

⚠️ 5-second throttle between requests (enforced). Plan batches accordingly.

When to use

Trigger phrases:

  • 全球新闻 / 全球事件 / 多语种新闻
  • 媒体 tone / sentiment trend / 国际关系
  • geopolitical / event tone
  • GDELT

Useful for "what is the world saying about X right now" — i.e. retrieving articles from non-English / non-financial sources that don't surface in Longbridge's curated newsfeed.

Workflow

  1. Build a query in GDELT DSL (the user's term, optionally with operators like domain:bloomberg.com, sourcelang:eng).
  2. Pick a mode:
    • artlist — list of articles (default).
    • timelinetone — per-15-min tone time series (-10 = very negative, +10 = very positive).
    • timelinevol / timelinevolinfo — article volume over time.
    • tonechart — tone histogram.
  3. Specify the window — prefer --start/--end (absolute), fall back to --timespan. The script converts relative timespans to absolute timestamps before the call and echoes them in meta.window so the journal can be re-run.

CLI examples

# Articles about Nvidia in the last 24h
python3 .claude/skills/gdelt/scripts/doc.py "Nvidia"

# 7-day window, English + Chinese articles about TSMC
python3 .claude/skills/gdelt/scripts/doc.py "TSMC OR \"Taiwan Semiconductor\"" --timespan 7d --lang eng,zho

# Tone timeline for Federal Reserve over 30 days
python3 .claude/skills/gdelt/scripts/doc.py "Federal Reserve" --mode timelinetone --timespan 30d

# Absolute window
python3 .claude/skills/gdelt/scripts/doc.py "AI chips" --start 20260501000000 --end 20260528000000

Output shape (artlist)

{
  "data": [
    {
      "url": "https://...",
      "title": "...",
      "seendate": "20260527T161500Z",
      "domain": "...",
      "language": "English",
      "sourcecountry": "United States",
      "socialimage": "..."
    }
  ],
  "meta": {
    "mode": "artlist",
    "query": "Nvidia",
    "window": { "start": "20260527071804", "end": "20260528071804" },
    "max_records": 75
  },
  "ok": true
}

Output shape (timelinetone)

{
  "ok": true,
  "data": [
    {"date": "20260520T000000Z", "value": 1.42},
    {"date": "20260520T001500Z", "value": 1.05},
    ...
  ],
  "meta": {"mode": "timelinetone", ...}
}

Error handling

Exit code Meaning LLM action
0 Success Parse data.
1 Invalid args (e.g. bad timespan / lang) Read hint.
3 HTTP 4xx / non-JSON response If body contains "Please limit requests", the throttle was tripped — wait and retry.
4 Network Suggest retry.

Known limitations

  • 5-second minimum between requests; batch tone + artlist queries must be sequenced.
  • --max-records cap is 250.
  • GDELT's tone metric is a heuristic — useful for direction-of-narrative, not ground truth.
  • Results are not cached (window-sensitive).

Related skills

  • longbridge-news for curated equity-specific newsfeed (Chinese-language UX).
  • sec-edgar for primary-source filings as the contrast to media narrative.
  • fred for macro data referenced in the narrative.
Files (kansoku)
  • scripts
    • doc.py 4.6 KB
      #!/usr/bin/env python3
      """GDELT 2.0 Doc API — global multilingual news with tone.
      
      GDELT is a rolling recent-window API, not a historical archive. Time windows
      must be specified absolutely (YYYYMMDDHHMMSS) to keep journal entries
      reproducible — relative `--timespan` is converted to absolute timestamps before
      the call.
      
      Throttle: ≥ 5 seconds between requests (enforced by _shared/client.py).
      Output: always JSON (we append format=json; the API defaults to HTML).
      """
      
      from __future__ import annotations
      
      import argparse
      import re
      import sys
      from datetime import datetime, timedelta, timezone
      from pathlib import Path
      from urllib.parse import urlencode
      
      ROOT = Path(__file__).resolve().parents[2]
      sys.path.insert(0, str(ROOT))
      
      from _shared import client  # noqa: E402
      
      GDELT_BASE = "https://api.gdeltproject.org/api/v2/doc/doc"
      
      LANG_ALLOWED = {
          "eng", "zho", "spa", "fra", "deu", "rus", "jpn", "kor", "ara", "por",
          "ita", "tur", "vie", "ind", "tha", "hin", "nld", "pol", "swe", "fin",
      }
      
      LANG_ALIAS = {
          "en": "eng", "zh": "zho", "ja": "jpn", "ko": "kor",
          "fr": "fra", "de": "deu", "es": "spa", "ru": "rus",
          "pt": "por", "it": "ita", "ar": "ara",
      }
      
      _TIMESPAN_RE = re.compile(r"^(\d+)([hHdDmM])$")
      
      
      def parse_timespan(spec: str) -> timedelta:
          m = _TIMESPAN_RE.match(spec)
          if not m:
              raise client.ClientError(
                  f"Invalid --timespan: {spec}",
                  exit_code=1,
                  hint="Use e.g. 24h, 7d, 1m (=30d)",
              )
          n, unit = int(m.group(1)), m.group(2).lower()
          if unit == "h":
              return timedelta(hours=n)
          if unit == "d":
              return timedelta(days=n)
          if unit == "m":
              return timedelta(days=30 * n)
          raise client.ClientError(f"Unknown unit: {unit}", exit_code=1)
      
      
      def fmt_gdelt_ts(d: datetime) -> str:
          return d.strftime("%Y%m%d%H%M%S")
      
      
      def normalise_langs(spec: str) -> list[str]:
          out = []
          for raw in spec.split(","):
              code = raw.strip().lower()
              code = LANG_ALIAS.get(code, code)
              if code not in LANG_ALLOWED:
                  raise client.ClientError(
                      f"Unsupported language code: {code}",
                      exit_code=1,
                      hint=f"Allowed: {', '.join(sorted(LANG_ALLOWED))}",
                  )
              out.append(code)
          return out
      
      
      def main() -> dict:
          p = argparse.ArgumentParser(description="GDELT 2.0 Doc API.")
          p.add_argument("query", help="Search query (GDELT DSL accepted).")
          p.add_argument(
              "--mode",
              choices=["artlist", "timelinetone", "timelinevol", "timelinevolinfo", "tonechart"],
              default="artlist",
          )
          p.add_argument("--start", help="Absolute start YYYYMMDDHHMMSS.")
          p.add_argument("--end", help="Absolute end YYYYMMDDHHMMSS.")
          p.add_argument(
              "--timespan",
              help="Relative window, e.g. 24h, 7d, 1m. Converted to abs start/end at call time.",
          )
          p.add_argument(
              "--lang",
              help="Comma-separated lang codes (eng, zho, jpn, ...). Mapped to sourcelang: query operator.",
          )
          p.add_argument("--max-records", type=int, default=75)
          p.add_argument("--smoke", action="store_true")
          args = p.parse_args()
      
          if args.smoke:
              return client.success({"status": "ok"}, smoke=True)
      
          query = args.query.strip()
          if args.lang:
              codes = normalise_langs(args.lang)
              if len(codes) == 1:
                  query = f"({query}) sourcelang:{codes[0]}"
              else:
                  joined = " OR ".join(f"sourcelang:{c}" for c in codes)
                  query = f"({query}) ({joined})"
      
          now = datetime.now(timezone.utc)
          if args.start and args.end:
              start_ts, end_ts = args.start, args.end
          elif args.timespan:
              delta = parse_timespan(args.timespan)
              start_ts = fmt_gdelt_ts(now - delta)
              end_ts = fmt_gdelt_ts(now)
          else:
              start_ts = fmt_gdelt_ts(now - timedelta(hours=24))
              end_ts = fmt_gdelt_ts(now)
      
          params = {
              "query": query,
              "mode": args.mode,
              "format": "json",
              "startdatetime": start_ts,
              "enddatetime": end_ts,
              "maxrecords": args.max_records,
          }
          url = f"{GDELT_BASE}?{urlencode(params)}"
          resp = client.fetch(url, source="gdelt", ttl=0)
      
          if args.mode == "artlist":
              items = resp.get("articles", []) if isinstance(resp, dict) else []
          elif args.mode in ("timelinetone", "timelinevol", "timelinevolinfo", "tonechart"):
              items = resp.get("timeline", resp) if isinstance(resp, dict) else resp
          else:
              items = resp
      
          return client.success(
              items,
              mode=args.mode,
              query=query,
              window={"start": start_ts, "end": end_ts},
              max_records=args.max_records,
          )
      
      
      if __name__ == "__main__":
          client.run(main)
      
  • SKILL.md 4 KB
    ---
    name: gdelt
    description: Global multilingual news event stream with tone scoring via GDELT 2.0 Doc API.
    ---
    
    # gdelt
    
    > Response language: match user input.
    
    > ⚠️ **GDELT is a rolling recent-window API**, not a historical event archive.
    > Time windows are anchored to absolute timestamps (UTC) for reproducibility —
    > the same query asked tomorrow will return different results.
    >
    > ⚠️ **5-second throttle** between requests (enforced). Plan batches accordingly.
    
    ## When to use
    
    Trigger phrases:
    
    - 全球新闻 / 全球事件 / 多语种新闻
    - 媒体 tone / sentiment trend / 国际关系
    - geopolitical / event tone
    - GDELT
    
    Useful for "what is the world saying about X right now" — i.e. retrieving
    articles from non-English / non-financial sources that don't surface in
    Longbridge's curated newsfeed.
    
    ## Workflow
    
    1. Build a query in GDELT DSL (the user's term, optionally with operators like
       `domain:bloomberg.com`, `sourcelang:eng`).
    2. Pick a mode:
       - `artlist` — list of articles (default).
       - `timelinetone` — per-15-min tone time series (-10 = very negative,
         +10 = very positive).
       - `timelinevol` / `timelinevolinfo` — article volume over time.
       - `tonechart` — tone histogram.
    3. Specify the window — prefer `--start`/`--end` (absolute), fall back to
       `--timespan`. The script converts relative timespans to absolute timestamps
       before the call and echoes them in `meta.window` so the journal can be
       re-run.
    
    ## CLI examples
    
    ```bash
    # Articles about Nvidia in the last 24h
    python3 .claude/skills/gdelt/scripts/doc.py "Nvidia"
    
    # 7-day window, English + Chinese articles about TSMC
    python3 .claude/skills/gdelt/scripts/doc.py "TSMC OR \"Taiwan Semiconductor\"" --timespan 7d --lang eng,zho
    
    # Tone timeline for Federal Reserve over 30 days
    python3 .claude/skills/gdelt/scripts/doc.py "Federal Reserve" --mode timelinetone --timespan 30d
    
    # Absolute window
    python3 .claude/skills/gdelt/scripts/doc.py "AI chips" --start 20260501000000 --end 20260528000000
    ```
    
    ## Output shape (artlist)
    
    ```json
    {
      "data": [
        {
          "url": "https://...",
          "title": "...",
          "seendate": "20260527T161500Z",
          "domain": "...",
          "language": "English",
          "sourcecountry": "United States",
          "socialimage": "..."
        }
      ],
      "meta": {
        "mode": "artlist",
        "query": "Nvidia",
        "window": { "start": "20260527071804", "end": "20260528071804" },
        "max_records": 75
      },
      "ok": true
    }
    ```
    
    ## Output shape (timelinetone)
    
    ```json
    {
      "ok": true,
      "data": [
        {"date": "20260520T000000Z", "value": 1.42},
        {"date": "20260520T001500Z", "value": 1.05},
        ...
      ],
      "meta": {"mode": "timelinetone", ...}
    }
    ```
    
    ## Error handling
    
    | Exit code | Meaning                                 | LLM action                                                                           |
    | --------- | --------------------------------------- | ------------------------------------------------------------------------------------ |
    | 0         | Success                                 | Parse `data`.                                                                        |
    | 1         | Invalid args (e.g. bad timespan / lang) | Read `hint`.                                                                         |
    | 3         | HTTP 4xx / non-JSON response            | If body contains "Please limit requests", the throttle was tripped — wait and retry. |
    | 4         | Network                                 | Suggest retry.                                                                       |
    
    ## Known limitations
    
    - 5-second minimum between requests; batch tone + artlist queries must be
      sequenced.
    - `--max-records` cap is 250.
    - GDELT's tone metric is a heuristic — useful for direction-of-narrative, not
      ground truth.
    - Results are not cached (window-sensitive).
    
    ## Related skills
    
    - `longbridge-news` for curated equity-specific newsfeed (Chinese-language UX).
    - `sec-edgar` for primary-source filings as the contrast to media narrative.
    - `fred` for macro data referenced in the narrative.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related