This is a submission for the Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady
What I Built
I wake up to a flatmates group with 40 messages, a family group with 120, and a college group I stopped reading in August. Somewhere in there is the one message that actually needs me: "can you be home Saturday morning for the wifi guy?"
sloth-digest reads WhatsApp chat exports and gives me two lines per chat each morning:
- What happened.
- What I need to do, or "Nothing for you."
It runs on a timer at 7am, so the digest is already waiting by the time I get up. The model runs on my laptop. My chats never go anywhere.
Demo
A fake flatmates chat goes in:
24/10/26, 21:02 - Asha: rent is due monday, everyone send me 9k by sunday pls
24/10/26, 21:05 - Ravi: done
24/10/26, 21:30 - Meera: wifi is down again, I called the ISP, tech comes saturday 10am
24/10/26, 21:31 - Meera: someone needs to be home for that
24/10/26, 22:10 - Ravi: not me, I'm out saturday
24/10/26, 22:40 - Asha: @Harsh can you be home sat morning?
24/10/26, 23:15 - Meera: also we're out of dish soap
And this comes out, as a desktop notification and in digests/2026-10-25.md. This is the raw output, junk prefix included:
## Flatmates (7 new)
1. Aft: Rent is due Monday, everyone needs to send 9k by Sunday.
2. Harsh: Be home Saturday morning.
Run it again without a new export and it says Nothing new.
Code
harshverma27
/
sloth-digest
Two-line morning digest of WhatsApp chats, summarised by a local model via Ollama
sloth-digest
Group chats move faster than I do. This reads WhatsApp chat exports and gives me two lines per chat each morning: what happened, and whether anyone needs me. A small model runs locally through Ollama, so the chats never leave my laptop.
Setup
Install Ollama and pull a small model:
ollama pull gemma3:1b
No pip installs. The script only uses the Python standard library.
Use
- In WhatsApp: open a chat, then More > Export chat > Without media. Drop the
.txtintoinbox/. On iOS you get a zip; unzip it and rename_chat.txtto the chat's name. - Run it:
python3 digest.py
You get a notification, plus digests/YYYY-MM-DD.md. state.json remembers how many messages it has already seen in each chat, so re-exporting the same chat tomorrow only summarises what's new.
Set ME at the top of digest.py to your name as it appears in chats, so…
How I Built It
The whole thing is about 100 lines of Python with no dependencies outside the standard library. The pieces:
Parsing. WhatsApp has no local API, but every phone can export a chat as a .txt. The catch is that Android and iOS write different formats, and newer versions sneak invisible Unicode in (a narrow no-break space before PM, a left-to-right mark before media lines):
Android: 12/10/26, 22:14 - Name: msg
iOS: [12/10/26, 10:14:05 PM] Name: msg
One regex matches the timestamp prefix for both. If the rest of the line has Name:, it's a message. If it doesn't, it's a system notice ("Asha changed the group name") and gets dropped. Lines with no timestamp are the continuation of a multi-line message. Media placeholders like <Media omitted> and image omitted are thrown away.
Only new messages. My first instinct was to store the last-seen timestamp. Then I remembered WhatsApp dates are DD/MM or MM/DD depending on your phone's locale, and there's no way to tell 03/04/26 apart. So I stopped parsing dates entirely. Every export contains the full history, which means I just store how many messages each chat had last time in state.json. Everything past that count is new. If the count shrinks, the file was replaced, so it starts over.
def new_messages(msgs, seen):
return msgs[seen:] if len(msgs) >= seen else msgs
The model. Gemma 3 1B, running in Ollama. The script POSTs to localhost:11434/api/chat with urllib, a low temperature, and a bigger context window than Ollama's default.
Telling the model who "me" is. My first run on the flatmates chat returned "Nothing for you." even though someone had @-mentioned me by name. The prompt said "what needs my attention" and the model had no idea who I was. Adding one setting fixed it:
ME = "Harsh"
PROMPT = (
f"You summarise a group chat for {ME}, who didn't read it. Reply with exactly two lines:\n"
"1. What happened: key topics and decisions.\n"
f"2. What {ME} needs to do: anything addressed to {ME} or @{ME}, and asks aimed at everyone. "
"Write 'Nothing for you.' if none.\n"
"No preamble, no extra lines."
)
After that it caught the Saturday ask in three out of three fresh runs.
Scheduling. A systemd user timer with Persistent=true, so if the laptop is asleep at 7am it runs the moment I open the lid. The README has the two unit files.
Tests. test_digest.py checks the parser against Android and iOS samples (including the invisible characters and a multi-line message) and checks the new-message count. It runs without Ollama.
Why Does Open Innovation Matter?
Group chats are some of the most private text I have. Flatmates arguing about rent, family health updates, friends venting. Sending all of that to a hosted API every night so it can tell me "rent is due" is a bad trade. With an open-weight model running locally, the text goes from a file on my disk to a process on my machine and back. That's the whole data flow.
It also costs nothing. A closed API would bill me every morning forever for a summary of my own messages. Gemma 3 1B is an 815 MB download, and after that it's free and works on a train with no signal.
And I can swap the model. A 1B model is fast but rough: you can see it start a line with "Aft:" in the demo above, and it doesn't always put "asks for everyone" in line 2. When that annoys me enough, I ollama pull a bigger model and change one string. No new account, no new pricing page.
Limits
- Exporting is still one tap per chat. WhatsApp doesn't let local apps read messages, and I didn't want to go near unofficial WhatsApp Web clients that log into your account.
- Only the newest 400 new messages per chat go to the model. That's plenty for one night. A big group after a week away gets trimmed.
Prize Categories
- Best Use of Gemma: Gemma 3 1B is the model doing the summarising, running locally through Ollama.
Top comments (0)