npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@drakulavich/uae-news-digest

v0.2.0

Published

Daily UAE news digest from Google News RSS with optional DeepL translation

Downloads

191

Readme

  • Signal first. Preferred sources and UAE-specific keywords push important stories up.
  • No repeat sludge. Exact keys plus fuzzy title matching collapse syndicated duplicates.
  • Human or machine. Read the emoji digest in your terminal, or pipe stable JSON into agents, cron, Slack, or your own scripts.
  • Bring your own feed. Use UAE by default, switch regions, or pass any RSS URL.
  • Translate only when you ask. Optional DeepL support keeps the default path simple and dependency-light.

Install

bun add -g @drakulavich/uae-news-digest

Or run from source:

git clone https://github.com/drakulavich/uae-news-digest.git
cd uae-news-digest
bun install
bun link

Usage

uae-news-digest                                      # fetch + print UAE news
uae-news-digest --hours 12 --limit 10                # tighter briefing window
uae-news-digest --json                               # stable envelope for automation
uae-news-digest --region de                          # Germany preset
uae-news-digest --rss-url http://localhost/feed.xml  # any RSS feed
uae-news-digest healthcheck                          # JSON liveness probe
DEEPL_AUTH_KEY=xxx uae-news-digest --target-lang DE  # optional DeepL translation

| Flag | Default | Description | |------|---------|-------------| | --region <code> | uae | News region preset (uae, us, uk, de) | | --hours <n> | 36 | Lookback window in hours | | --limit <n> | 6 | Max items in digest | | --target-lang <code> | | DeepL target language (e.g. DE, FR, JA). Requires DEEPL_AUTH_KEY | | --rss-url <url> | | Custom RSS URL (overrides --region) | | --state-file <path> | ./seen_titles.txt | Seen-items state file | | --timeout-ms <n> | 15000 | RSS fetch timeout | | --topics-config <path> | | Path to topics config JSON (overrides auto-detect) | | --no-topics | | Force legacy region mode even if a topics config is present | | --dry-run | false | Preview without updating state | | --json | false | Output as JSON (agent-friendly envelope) |

What You Get

🇦🇪 UAE Latest News Digest

🛡️ UAE intercepts 79 Iranian strike assets (The National, 2h ago)
📉 Dubai property sales drop more than 30% (Anadolu Ajansı, 5h ago)
⛴️ Container ship incident at Khor Fakkan (Reuters, 3h ago)
✈️ Abu Dhabi airport reopens after rain (Khaleej Times, 1h ago)
🌧️ Unstable weather hits some emirates (Gulf News, 4h ago)
🛢️ Oil prices: OPEC+ mulls output increase (CNBC, 6h ago)

And when you need structured output:

uae-news-digest --json | jq '.items[].title'

Signal filter

Every run automatically surfaces headlines that materially affect an expat family in the UAE across four areas:

  • Safety / threats — missiles, drones, airspace closures, evacuation alerts, storms
  • Money / daily life — rent, fees, fuel prices, salary, fines, subsidies
  • Rules / visas / documents — visa changes, new laws, permit and licence updates
  • Logistics / infrastructure — flight disruptions, road closures, metro outages

Items that score above the threshold are pulled into a 🚨 Important block printed at the top of the digest, deduplicated from the regular listing below. Each important line shows a [signals] marker that explains why it surfaced. PR puff (launches, awards, "world's first/tallest/largest", festivals, and similar) is penalised and pushed down.

This is a heuristic — no API key required, always on.

Example output (topics mode):

🇦🇪 UAE digest — 2025-11-14

🚨 Important
  🛡️ UAE intercepts Iranian missile barrage (The National, 1h ago) [missile, airspace] — UAE Security
  ✈️ Dubai airport closes Terminal 2 for maintenance (Gulf News, 3h ago) [flight] — Travel

💰 UAE economy
  📉 OPEC+ weighs output cut for Q1 (Reuters, 4h ago)
  ...

In region mode the Important block has the same layout but without the — Topic Name suffix.

Topic match / matchMode

Google News sometimes returns loosely-matched articles for a query. Add optional match and matchMode keys to any topic to require real keyword matches in the article title:

{
  "topics": [
    {
      "slug": "schools",
      "name": "Schools",
      "query": "school fees Dubai",
      "match": ["school", "fees"],
      "matchMode": "all"
    }
  ]
}

| Key | Type | Default | Description | |-----|------|---------|-------------| | match | string[] | — | Keywords that must appear in the article title. Omit to keep existing behaviour. | | matchMode | "all" | "any" | <N> | "all" | "all" — every term must match; "any" — at least one; a positive integer N — at least N terms. |

When articles are dropped for failing the keyword filter, a warning reports how many were dropped (visible in non-JSON mode on stderr). The CLI equivalents for region mode are --match <terms...> and --match-mode <mode>.

Agent workflow (key-free smart pass)

--json enriches every item with importance, tier (breaking | impact | neutral | fluff), signals, and matchedTerms. Pipe that to an LLM with the ready-made filter criterion:

uae-news-digest --prompt                                    # print the filter criterion
uae-news-digest --json --dry-run | claude "$(uae-news-digest --prompt)"

--dry-run keeps the seen-items state file untouched, so an ad-hoc agent-filter pass doesn't mark those articles as seen and hide them from the next real digest. Drop it when this pipe is the scheduled run.

The agent can drop noise reproducibly based solely on the structured metadata — no extra API key, no custom prompt engineering required.

--prompt output:

You are a news filter for an expat family in the UAE. Keep only what materially affects safety, money, rules/visas, or logistics. Drop PR, launches, awards, rankings, and 'world's first/tallest/largest'.

Topics Mode

For per-topic digests (e.g. economy, real estate, regional politics) instead of one undifferentiated regional feed, create a digest.config.json file:

{
  "locale": { "hl": "en", "gl": "AE", "ceid": "AE:en" },
  "topics": [
    {
      "slug": "economy",
      "name": "UAE economy",
      "emoji": "💰",
      "query": "(UAE OR Emirates) AND (economy OR GDP OR inflation OR ADNOC OR non-oil)",
      "limit": 5
    },
    {
      "slug": "realty",
      "name": "Real estate",
      "emoji": "🏠",
      "query": "(Dubai OR \"Abu Dhabi\") AND (\"real estate\" OR property OR Emaar OR Aldar)",
      "limit": 4
    },
    {
      "slug": "chocolate",
      "name": "Dubai chocolate",
      "emoji": "🍫",
      "query": "\"Dubai chocolate\" OR pistachio OR kunafa",
      "limit": 3
    }
  ]
}

The CLI looks for the config in this order:

  1. --topics-config <path> (explicit override)
  2. ./digest.config.json (current working directory)
  3. $XDG_CONFIG_HOME/uae-news-digest/topics.json (falls back to ~/.config/uae-news-digest/topics.json)

When a config is found, the CLI fetches each topic in parallel and renders one section per topic. Cross-topic dedup is "first topic in config wins" — reorder the array to set priority. To force the legacy region mode while a config is present, pass --no-topics.

--target-lang translates all section titles in a single DeepL batch. --limit, when explicitly set, overrides every topic's per-topic limit for the run.

The example query strings above are starting points, not optimal — iterate them against real Google News output.

Topics-mode JSON output

{
  "tool": "uae-news-digest",
  "version": "...",
  "mode": "topics",
  "query": { "hours": 36, "targetLang": null },
  "topics": [
    { "slug": "economy", "name": "UAE economy", "count": 5 },
    { "slug": "realty",  "name": "Real estate", "count": 4 },
    { "slug": "chocolate", "name": "Dubai chocolate", "count": 1 }
  ],
  "count": 10,
  "warnings": [],
  "items": [
    { "topic": "economy", "title": "...", "source": "...", "score": 0, "publishedAt": "...", "hoursAgo": 4 }
  ]
}

Items are emitted as a flat list in section order; each carries its topic slug so consumers can group. Legacy region mode emits the same envelope with mode: "region" and without the topics array.

Programmatic API

import { parseRss, buildDigest, runDigest, renderDigest } from "@drakulavich/uae-news-digest/core";

Use the core API when you already have RSS XML and want the same filtering, scoring, deduplication, translation fallback, and rendering logic without spawning the CLI.

How It Works

uae-news-digest --region us --hours 12 --limit 10
  │
  ├── Region ────── resolve RSS URL from preset (or --rss-url override)
  │
  ├── Fetch RSS ─── Google News RSS feed
  │
  ├── Filter ────── skip: opinion, tabloids, sports, travel
  │
  ├── Score ─────── +3 preferred source, +2 UAE mention, +2 priority topic
  │
  ├── Deduplicate ─ exact key match + Jaccard fuzzy (threshold 0.45)
  │
  ├── Translate ─── DeepL API (optional, when --target-lang set)
  │
  └── Render ────── region flag + emoji + title + source + hours ago

State file (seen_titles.txt) tracks seen articles so scheduled runs do not repeat the same briefing forever.

healthcheck uses the default live Google News RSS feed. For deterministic smoke tests, pass healthcheck --rss-url <url>.

Requirements

  • Bun >= 1.3 (CI pins 1.3.14).
  • CI validates Linux and macOS. Windows is not a supported target yet.

License

Made with 💛🩵 Published under MIT License.