DEV Community

Samar Nathani
Samar Nathani

Posted on AI-assisted

devpulse: A Local-AI News Digest Built for One Person

Hacktoberfest Weekend Challenge: Build for a Friend Submission 🤝

This is a submission for the Hacktoberfest Weekend Challenge: Build for a Friend

What I Built

devpulse is a Discord bot built for my friend Soham.

His problem is simple, and you've probably felt it too: everything moves too fast now. Releases ship while you're looking the other way, the AI news cycle turns over before lunch, and Soham kept missing the things he actually wanted to see — new versions of the tools he uses, interesting projects before everyone else finds them. Keeping up had quietly become a part-time job nobody applied for.

So devpulse does the scanning for him. Every morning it pulls rising GitHub repositories, new releases from our shared watchlist, Hacker News, and dev.to, judges every single item with a model running on my laptop, and posts one clean message to our Discord:

  • TOP 5 FOR YOU — ranked by relevance and quality, with a two-line verdict on each
  • SLEEPER PICK — high quality, low attention: the thing worth finding before it's everywhere
  • NEW RELEASES — just the watchlist repos, linked straight to the release page

One message. No algorithm, no ads, no forty tabs. It lands at 7 AM, before either of us is fully awake, and the watchlist is just one config line — easy to repoint whenever Soham starts following something new.

I handed him the live bot in our Discord. His response, from the channel:

demnnn this looks pretty good , for a project made in a day this is pretty good , if you optimize it more then this can be fire and if you want to you can deploy it also so evryne will be able to use it but yeah personally ill be using this aand will wait for more updates from you

Demo

The whole thing in action - 2:40, no voiceover, just the sequence: refresh, offline cache, the crash with the terminal visible, recovery, /sleeper, then a friend's reaction in the channel before one last run:

https://youtu.be/MDPMhYyqOjs

It fails honestly too. When I stopped Ollama mid-day and ran /refresh, the bot didn't pretend things were fine — it posted a skip message, recorded the failed run, and picked the work back up automatically when the model returned.

The terminal tells the same story from the inside: every pending item logged judge failed for '<item>': [WinError 10061] No connection could be made because the target machine actively refused it, while Discord only ever saw the honest skip message.

Want to poke it yourself: join the server and try /sleeper, /releases, /dig fastapi, or /status.

Code

GitHub logo SammySN-car / devpulse

Discord bot: local qwen2.5:7b judges fresh tech news into a daily digest with sleeper picks. Built for Hacktoberfest 2026 Weekend Challenge.

devpulse

A Discord bot that gathers what is moving in tech - new releases, rising GitHub repos, Show HN posts, dev.to articles - and judges every item with an open-weight model (qwen2.5:7b) running locally through Ollama. Each day it posts one digest: the top 5 items for your stack, one sleeper pick (high quality, low hype scored by arithmetic, not vibes), and new releases from your watchlist.

Why open

Everything runs on-device: collectors call free public APIs, the judge runs on your own machine, and results live in a local SQLite file. No cloud inference, no subscription, nothing you fetch leaves the machine.

Setup

  1. Install Python 3.11+ and Ollama; then ollama pull qwen2.5:7b
  2. Create a Discord application, invite the bot, copy the token
  3. python -m pip install -e ".[dev]"
  4. Copy .env.example to .env and fill in token, channel id, owner id, watchlist
  5. devpulse - the digest posts at DIGEST_TIME…

Python 3.13 · discord.py · httpx · SQLite · pytest · ruff · GitHub Actions CI (Ubuntu + Windows). 61 tests, hermetic by default (no network, no model), plus one live marker test that does a real model round-trip when I ask for it.

How I Built It

The open-source AI at the core is qwen2.5:7b running locally through Ollama. No API key, no per-call bill, no request leaves the machine. The model reads each item and returns JSON: a relevance score, a quality score, and a two-line verdict ("what it actually is; worth clicking yes/no").

What the architecture looks like:

GitHub / HN / dev.to (free sources)
        │  collect
        ▼
  normalizer ── URL identity, dedupe, within-source engagement percentile
        │
        ▼
    SQLite ── items + judgments + digest ledger
        │  pending items only (never re-judges)
        ▼
  qwen2.5:7b via Ollama ── sequential, JSON mode, temperature 0.2
        │                    keep_alive: 0 → model evicted after each batch
        ▼
  composer ── TOP 5, sleeper pick, releases, 2000-char Discord limit
        │
        ▼
   Discord ── 5 slash commands + a 07:00 scheduler
Enter fullscreen mode Exit fullscreen mode

Details I care about:

  • A RAM guard trips before anything can hurt the machine. If free memory drops below 1500 MB, judging stops cleanly, the reason is surfaced in the digest, and the run is recorded — leftovers are retried automatically the next day.
  • The sleeper pick is a formula, not an opinion: quality × (1 − engagement_percentile), with an eligibility gate (quality ≥ 7, engagement bottom 40%). A q8 repo at 14% engagement ranks below a q7 repo at 0% — high signal, no attention.
  • Judging failures never lie. One retry, then the item stays pending; if every judgment fails, the digest says so instead of pretending it's a quiet news day.
  • The model sleeps after every batch (keep_alive: 0) — a 4.7 GB resident model on a normal laptop is not a tradeoff I was willing to make 24/7.

Process, since judges asked to see it: I wrote the spec first, cut it into 13 tasks, and implemented each with tests written before code, followed by an independent spec review and quality review per task. The final whole-branch review caught a real production bug the test suite couldn't see: the SQLite connection was created on the main thread but digests run on a worker thread (asyncio.to_thread) — every scheduled run in production would have failed with SQLite objects created in a thread can only be used in that thread, while all 48 tests at the time stayed green. Fixed with a reentrant lock across every connection path, plus a cross-thread regression test. CI runs on Ubuntu and Windows, and a live round-trip test proved the real model answers and gets evicted afterward.

Why Does Open Innovation Matter?

This project is made of open pieces, and each one earns it:

  1. It runs where the data lives. The digest is a reading habit — who and what someone follows is a profile. Here, fetches happen locally, judgments happen locally, and storage is a file on disk. A closed API would mean sending every title and description I care about to a third party, every morning, forever.
  2. It runs with no internet. After the model is pulled once, judging works on a plane, on a train, behind a captive portal. The only thing that needs the network is the news collection itself.
  3. It costs nothing per decision. 33 items judged in 4.5 minutes. On a closed API that's 33 calls, 33 bills, rate limits, and an API key sitting in a .env file that can leak. Here it's electricity.
  4. I could swap the model in one line. I ran a spike comparing llama3.2 (3B) against qwen2.5:7b before committing: the small model echoed titles back as "verdicts" on 6 of 15 items; qwen produced real evaluations with a yes/no. Because everything behind the judge is an open interface, that decision was mine to measure, not a vendor's roadmap. The spike table is in the repo.
  5. Where open beat closed outright: resilience. When the model was down mid-run, my bot degraded into an honest skip message and retried tomorrow. Try getting that failure semantics from a vendor status page.

The honest tradeoff of open here is speed — a 4.5-minute batch is not a streaming UI. For one digest a day, who cares.

Top comments (2)

Collapse
 
anika_jha_33ae1d9afc69178 profile image
Anika Jha •

Really cool build!! Love the local AI approach and the honest failure handling. The sleeper-pick idea is especially neat!!

Collapse
 
sammysncar profile image
Samar Nathani •

Thanks bro ✌️