My WhatsApp Was My Open-Mic Booking System, So I Gave It to Gemma
This is a submission for the Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady What I Built I'm a stand-up comic in Bangalore, and I curate the Monday and Tuesday open-mic lineups at a club. Wha
This is a submission for the Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady
What I Built
I'm a stand-up comic in Bangalore, and I curate the Monday and Tuesday open-mic lineups at a club.
WhatsApp is my daily driver. I use it to ask other rooms for spots, gossip about shows and audience with comics, reply to my family group, talk to my girlfriend and somewhere in all of that, comics message me asking for a spot on the lineups I curate. Every new message pushes the older ones down. Some requests just slip out of view, and that isn't fair to the comic who asked on Saturday morning.
So every week, to keep it fair, I scroll back through every chat, work out who actually asked, check who performed last week (they go to the back of the queue), decide if last week's cancelled mic gets repeated, message everyone, and add and remove people from each show's WhatsApp group by hand.
It takes me about 3 hours every week. I'd rather spend those 3 hours writing jokes. Or sleeping. Mostly sleeping.
So I built Spot Solutions. A Gemma model running on my old laptop reads my DMs, picks out the real spot requests, and drops them on a lineup board. I drag names into shows, press send, and the confirmations, polite regrets and group updates take care of themselves.
Nothing goes out without my OK. I wanted to be lazy, not crazy.
Demo
Live app: spot-solutions.web.app. Hit Try the live demo and you get your own sandbox of a curator's week. No sign-up. Drag comics around, edit the messages, reset a lineup, break things. It's yours.
(Watch it as a video, 25 seconds.)
Here's the weekly loop:
- Comics DM me like they always have. Nothing changes for them.
- The server on my laptop waits until a chat goes quiet, then hands the whole burst to Gemma. So "hey" ... "monday?" ... "got a new 5" counts as one request, not three.
- Gemma decides what each conversation is: a spot request (with preferred days), a cancellation, "am I up this week?", a question about the venue, or noise.
- Requests land in Waiting on the board. Last week's performers sink to the bottom with a tag.
- I drag comics into Monday and Tuesday, hit Confirm & send, and review who gets added to and removed from each show's group before anything changes.
It's built for the phone, because that's where I actually curate:
The Lazy Payoff
What Spot Solutions takes off my plate every week:
- finding the actual requests in a weekend of DMs
- checking who performed last week, who asked for which day, and whether a cancelled mic repeats
- messaging every comic, the ones who got a spot and the ones who didn't
- adding and removing people from each show's group
Before: about 3 hours a week. Now: about 15 minutes, most of it spent dragging names around.
That's about 12 hours a month back. It also runs for next to nothing, on a laptop that was gathering dust.
This isn't my first attempt either. Version 1 ran for 7 months across 2024 and 2025 on Google Apps Script, Google Sheets and Twilio: 2,200+ spot requests from 150+ comics. It worked, but it needed rigid keywords, cost money per message, and every rule lived in spreadsheet formulas. Version 2 keeps all the rules V1 learned (request windows, skip-last-week, ban and ignore lists, "did the mic happen?" checks, last-minute spot offers) and lets Gemma handle the messy human part.
How I Built It
- WhatsApp: whatsapp-web.js in a Node worker. One headless WhatsApp Web session per curator.
- AI: Gemma 3 (4B) through Ollama, on the same laptop. Gemini is an optional fallback, off unless you add a key.
- App: React + Vite with Material 3, hosted on Firebase. Works on a phone, and installs as a PWA.
- The glue: Firestore. The web app writes commands, the worker listens and writes results back. The laptop only makes outbound connections. No open ports, no tunnel, no static IP. It works on home Wi-Fi while I'm on mobile data across town.
The part where the small model broke
Comics ask for spots in every way imaginable:
"bhai monday ka spot milega? naya 5 hai"
"put me down pls"
"any day works, got new bits"
"guru tuesday sigutta?"
And my WhatsApp isn't only comics. It's bank alerts, UPI receipts, OTPs, delivery updates, forwarded memes and audience members asking about tickets. A 4B model asked "is this a spot request?" will say yes with confidence to things that clearly aren't, and a confidence score alone didn't protect against that.
What worked was making the model quote the ask. Every classification has to include an evidence field copied from the messages, and the worker checks that quote is really there:
/** The model must quote the ask. If its "evidence" isn't actually in the messages, it made it up. */
export function evidenceIsReal(evidence: string, messages: string[]) {
const ev = norm(evidence);
if (!ev) return false;
const text = norm(messages.join(" "));
if (text.includes(ev)) return true;
const words = ev.split(" ").filter((w) => w.length > 1);
const hits = words.filter((w) => text.includes(w)).length;
return words.length > 0 && hits / words.length >= 0.75;
}
If the quote isn't in the chat, the classification gets thrown out as noise. The model can't invent a request out of a bank alert when it has to point at the words. The 75% word match is loose on purpose: Hinglish has no fixed spelling, so a quote can drift a little from the original and still be honest.
Auto-acknowledging ("got your request, will confirm soon") is off by default. When it's on, it needs a quoted ask at 85%+ confidence, in a message under 10 minutes old, from a real person and never a business account.
Making an old laptop a real server
Most of the second half of the week went into making the server boring to run:
- One installer each for Ubuntu (even from a USB stick), Windows and macOS. The Mac gets a small menu-bar app.
- It survives restarts, power cuts and closed lids. The WhatsApp login gets backed up, and when the server comes back it catches up on DMs that arrived while it was down, without auto-replying to any of them.
- Restarting the worker, restarting Ollama, reading logs and approving new curators all happen from the app's Server page, so after setup I never need to touch the laptop.
- It's invite-only. Other curators sign in, I approve them, and they link their own WhatsApp. An 8 GB laptop should comfortably host about 6 curators.
Why Open Matters Here
My comics' DMs are private. People send me their phone numbers, their sets, their bad days. Sending every one of those messages to a cloud API so a model can decide whether "bro monday?" is a spot request felt wrong.
With Gemma running locally:
- No cloud model reads the chats. Gemma reads them on my own laptop, and messages that aren't spot requests aren't even stored.
- The AI costs nothing. No per-message bill, which matters when a busy weekend means hundreds of messages.
- I can swap the model. Gemma 3 1B on a weak machine, 12B on a better one, or Gemini as a fallback when I choose. Each curator gets a setting for this: "Local only" or "Local first".
- Old hardware gets a second life. The laptop running this was headed for a drawer.
And it's all MIT licensed. Another open-mic scene can run its own server for its own comics, and nothing about it depends on me.
Several Laptops, One Lineup
One laptop is a single point of failure, and old laptops fail. So Spot Solutions runs on as many servers as you want, all pointed at the same project: a Mac, a Windows laptop, an Ubuntu box in a cupboard.
Each WhatsApp account runs on exactly one of them at a time. That server holds a lease and renews it every 30 seconds. If it goes quiet (power cut, closed lid, a cat on the keyboard), it stops its own WhatsApp first, and only after that can another server take the account over.
The new server doesn't ask for a QR scan. It pulls an encrypted copy of the WhatsApp login from Cloud Storage, unlocks it, and carries on. Every copy gets a version number, and the newest copy always wins, so a laptop that comes back after a week can't overwrite a fresher login with its old one.
Accounts go to the least busy server, curators can pin themselves to one, and a curator who picks "Local only" only ever lands on a server running Gemma. Each machine gets its own key, so if a laptop gets stolen I revoke that one key and the rest keep running.
Code
amrit-contour
/
spot-solutions
Open-mic lineup curation on autopilot: WhatsApp spot requests triaged by local Gemma, a lineup board, and a server that runs on any old laptop.
Spot Solutions
Open-mic lineup curation on autopilot. Comics DM you for spots on WhatsApp; a local Gemma model works out who's actually asking, acknowledges them, and drops them into a dashboard where you drag names into shows and hit send. Everyone who didn't make it gets a polite regret, and each show's WhatsApp group is updated to match the lineup.
Built for the DEV Hacktoberfest 2026 Open-Source AI Challenge (Week 3: Slow and Steady).
How it works
Web app (React, Firebase Hosting) โโโถ Firestore โโโ Worker on your laptop
Google sign-in, lineup board realtime whatsapp-web.js (1 session / curator)
writes commands + config uid-scoped Ollama ยท gemma3:4b (Gemini API fallback)
The worker only makes outbound connections. No ports, no tunnel. The web app drops commands into Firestore and the worker executes them.
- Intake: inbound DMs are debounced per chat (45s), so "hey" โฆ "monday?" โฆ "got a new 5"โฆ
Prize Categories
- Best Use of Gemma: Gemma 3 runs every AI decision in the app, locally through Ollama: reading bursts of Hinglish, English and Kannada DMs, sorting them into spot requests, cancellations, questions and noise, pulling out preferred days, and quoting the ask so nothing gets invented. Curators who pick "Local only" never touch a cloud model at all.
Built with Gemma 3, Ollama, whatsapp-web.js, React and Firebase, mostly by a sloth with a laptop and a AI subscription.
Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes โ full credit and traffic to the original publisher.




