# Build your own AI radar in 20 minutes

You'll end up with a private robot that runs every morning on GitHub's servers, reads 50 sources, scores ~400 items, and sends you the 10 that matter on Telegram (or email). It costs $0 and you never have to open a laptop for it again.

You need: a GitHub account (free), and Telegram on your phone **or** an email address. You don't need to know how to code.

| Step | What | Time |
|---|---|---|
| 0 | (Optional) Test it on your computer | 3 min |
| 1 | Create a private GitHub repo and upload 3 files | 5 min |
| 2 | Add the workflow file | 3 min |
| 3 | Create your Telegram bot (or email key) | 5 min |
| 4 | Add 2 secrets and run it | 3 min |
| 5 | Make it yours (keywords) | 1 min |

---

## Step 0 · Optional: test it on your computer (3 min)

If you have Node.js 20 or newer (`node --version` in Terminal), open a terminal in this `radar` folder and run:

```bash
node radar.mjs --dry-run
```

In about 5 seconds it prints today's digest. Nothing is sent and no file is written. If you see `# AI Radar · …` followed by 10 items, everything works. No Node? Skip this step: GitHub will run it for you.

Useful flags:

```bash
node radar.mjs --dry-run --verbose      # also shows how many items each source returned
node radar.mjs --dry-run --only=hn      # test one source type: hn, github-search, github-trending, huggingface, rss, reddit
node radar.mjs --dry-run --no-memory    # ignore what it already showed you
```

## Step 1 · Create the repo and upload the files (5 min)

1. Go to <https://github.com/new>.
2. Repository name: `ai-radar`. Choose **Private**. Tick **Add a README file**. Click **Create repository**.
3. Click **Add file → Upload files**. Drag in these three files from the `radar` folder: `radar.mjs`, `sources.json`, `config.json`. Click **Commit changes**.

> Private is the right default: your digests and keywords stay yours. A private repo on the free plan includes 2,000 Actions minutes a month; this job uses about 1 minute a day (~30 a month). Public repos run Actions for free with no minute cap.

## Step 2 · Add the workflow file (3 min)

Mac and Windows hide folders that start with a dot, so drag-and-drop often skips `.github/`. Create it by hand instead:

1. In your repo, click **Add file → Create new file**.
2. In the name box type exactly: `.github/workflows/radar.yml` (the slashes create the folders).
3. Paste this, then click **Commit changes**:

```yaml
name: AI Radar

on:
  schedule:
    - cron: "0 7 * * *" # every day at 07:00 UTC — change it if you like
  workflow_dispatch: {}

permissions:
  contents: write

concurrency:
  group: ai-radar
  cancel-in-progress: false

jobs:
  radar:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v7

      - uses: actions/setup-node@v7
        with:
          node-version: 22

      - name: Scan, score and send the digest
        run: node radar.mjs
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
          TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
          RESEND_API_KEY: ${{ secrets.RESEND_API_KEY }}
          EMAIL_TO: ${{ secrets.EMAIL_TO }}
          EMAIL_FROM: ${{ secrets.EMAIL_FROM }}

      - name: Save digest + memory to the repo
        if: always()
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
          mkdir -p digests state
          git add digests state
          git diff --staged --quiet || (git commit -m "radar: digest $(date -u +%F)" && git push)
```

(The same file is in this kit at `radar/.github/workflows/radar.yml`.)

**Pick your time.** Cron uses UTC. `0 7 * * *` = 07:00 UTC = 09:00 in Italy in summer, 03:00 in New York. For 08:00 New York (EDT) use `0 12 * * *`. GitHub can start scheduled jobs a few minutes late when its servers are busy. That's normal.

## Step 3 · Create your delivery channel (5 min)

### Option A: Telegram (recommended — it lands on your phone)

1. In Telegram, open **@BotFather** (blue check mark). Send `/newbot`.
2. Give it a name (`My AI Radar`) and a username ending in `bot` (`myname_radar_bot`).
3. BotFather replies with a **token** like `7123456789:AAH...`. Copy it. Treat it like a password.
4. Open your new bot (BotFather sends the link) and press **Start**, then send it any message ("hi").
5. In your browser, open this URL with your token pasted in:
   `https://api.telegram.org/bot<YOUR_TOKEN>/getUpdates`
6. Find `"chat":{"id":123456789` in the text. That number is your **chat ID**.

Want the digest in a Telegram **channel** instead (to share with your team or audience)? Create the channel, add your bot as an **admin** with "Post messages", and use `@yourchannelname` as the chat ID (public channels) or the `-100…` ID from `getUpdates` after posting once in the channel.

### Option B: Email (via Resend, free tier)

1. Sign up at <https://resend.com>. The free plan covers 3,000 emails a month (max 100 a day); you need one a day.
2. Go to **API Keys → Create API Key**. Copy it (`re_...`).
3. Without your own domain you can send from `onboarding@resend.dev`, but only to the email address you signed up with. That's all you need for a personal digest. To send to other people, verify a domain in Resend and set `EMAIL_FROM` to an address on it.

### Option C: Just the markdown file

Do nothing. Every run saves `digests/YYYY-MM-DD.md` and `digests/latest.md` in your repo. Star the repo and open `latest.md` with your coffee.

## Step 4 · Add the secrets and run it (3 min)

1. In your repo: **Settings → Secrets and variables → Actions → New repository secret**.
2. Add the ones you need (name on the left, exactly as written):

| Secret | Value | Needed for |
|---|---|---|
| `TELEGRAM_BOT_TOKEN` | the token from BotFather | Telegram |
| `TELEGRAM_CHAT_ID` | your chat ID (or `@channelname`) | Telegram |
| `RESEND_API_KEY` | `re_...` | Email |
| `EMAIL_TO` | your email (comma-separate several) | Email |
| `EMAIL_FROM` | optional, e.g. `Radar <radar@yourdomain.com>` | Email with your own domain |

You don't add `GITHUB_TOKEN`: GitHub provides it automatically (it raises the GitHub search limits).

3. Go to the **Actions** tab. If GitHub asks, click **I understand my workflows, go ahead and enable them**.
4. Click **AI Radar → Run workflow → Run workflow**. Wait ~40 seconds. Green check = your first digest is on your phone.

From now on it runs every morning by itself.

## Step 5 · Make it yours (1 min, then whenever you want)

Open `config.json` on GitHub (click it → pencil icon) and change the `keywords` list. Each line is a pattern and a weight:

```json
{ "match": "\\bshopify\\b|\\becommerce\\b", "weight": 1.2 }
```

- `\\b` means "whole word". `|` means "or". Matching is case-insensitive.
- Weight 1.0–1.3 = your core niche. 0.5 = related. 0.2 = weak hint.
- Things you never want to see go in `negative` (they subtract) or `noise` (dropped entirely).

Other knobs in `config.json → digest`:

| Setting | Default | What it does |
|---|---|---|
| `top` | 10 | items in the main list |
| `alsoMoving` | 5 | short "also moving" list |
| `maxPerSource` | 3 | max items from one source in the top list (keeps variety) |
| `minScore` | 40 | items under this never show up |
| `maxAgeDays` | 3 | ignore blog posts older than this |
| `rememberDays` | 10 | never repeat an item it showed you in this window |

To add a source, open `sources.json` and copy a line in `feeds`. Most sites have a feed at `/feed`, `/rss`, `/rss.xml` or `/atom`. Every GitHub repo has one at `https://github.com/OWNER/REPO/releases.atom`. Every YouTube channel has one at `https://www.youtube.com/feeds/videos.xml?channel_id=CHANNEL_ID`. Add `"filter": true` for broad sources so only on-niche posts get through.

---

## Troubleshooting

| You see | Fix |
|---|---|
| Red ✗ with `Telegram: Unauthorized` | Token is wrong. Copy it again from @BotFather (`/mybots → API Token`). |
| `Telegram: Bad Request: chat not found` | You didn't press **Start** on your bot, or the chat ID is wrong. For channels, the bot must be an admin. |
| `Resend: HTTP 403` | With `onboarding@resend.dev` you can only send to your own signup email. Verify a domain to send elsewhere. |
| `Source health: X: HTTP 403 / timeout` in the digest | That one source blocked or was slow today. The rest still works. Disable it in `sources.json` if it keeps failing. |
| Push failed / `permission denied` on the save step | Repo **Settings → Actions → General → Workflow permissions → Read and write**, or keep the `permissions: contents: write` block in the YAML. |
| The schedule stopped | GitHub turns off scheduled workflows in public repos after 60 days without activity. Actions tab → AI Radar → **Enable workflow**. |
| "Nothing scored above 40 today" | Your keywords are too narrow. Add more, or lower `minScore`. |
| Reddit is missing | It's off by default. Reddit blocked its anonymous JSON in 2026 and its public RSS is usually blocked from cloud servers. Set `"enabled": true` to try; if it fails, the run continues without it. |

## What's in this folder

```
radar/
├── radar.mjs                  # the whole radar: ~650 lines, zero dependencies
├── sources.json               # 45 RSS feeds + 6 API sources (verified 2026-10-04)
├── config.json                # your keywords, noise filters, digest settings
├── .github/workflows/radar.yml
├── digests/example-2026-10-04.md   # a real digest from a test run
└── state/seen.json            # memory, so you never get the same item twice
```

Made by Dan Shipped (@danshipped). MIT license.
