QuietBox 以本機 Gemma 識別未開啟電子報,經用戶確認後協助退訂
QuietBox: Unsubscribe from newsletters you never open
開發者 Aravind Sathesh 製作了 QuietBox,一款 macOS 選單列 App,使用在 Mac 本機運行的 Gemma 識別長期未開啟的電子報及推廣電郵,並由用戶確認後再退訂。
This is a submission for the Hacktoberfest Open-Source AI Challenge Week 3
What I Built
My inbox kept filling up with newsletters I'd signed up for months ago and promotional emails I rarely, if ever, opened. I wanted a simple way to clean it up without handing over access to my entire mailbox to a third-party service.
That's what led me to build QuietBox.
QuietBox is a native macOS menu bar app that identifies newsletters and promotional emails you consistently ignore and helps you unsubscribe from them. It uses a Gemma model running locally on your Mac, so there's no need to send email data to a hosted AI service for classification.
The idea is simple: let the app do the tedious work of finding unwanted emails, but leave the final decision to you.
QuietBox scans email headers from the past 12 months and groups messages by sender. It looks for senders that meet three criteria: at least three emails received, an unopened rate of 90% or higher, and no replies or starred messages.
These rules narrow down the candidates, but they don't tell the whole story. Plenty of emails are worth keeping even if you rarely open them. Bank alerts, receipts, shipping updates, and security notifications are good examples.
That's where Gemma comes in. Using Ollama, QuietBox evaluates the candidate senders and helps distinguish newsletters you might want to unsubscribe from from emails you should probably keep.
Once a week, the app sends a notification, such as "7 senders you never open." You can review the suggestions, untick any senders you want to keep, and click Unsubscribe to proceed.
How Unsubscribing Works
QuietBox uses the simplest available option for each sender:
- One-click unsubscribe: If the sender supports RFC 8058 one-click unsubscribe, QuietBox sends the required POST request to the advertised endpoint.
-
Unsubscribe email: If the sender provides a
mailto:unsubscribe link instead, QuietBox sends an unsubscribe email. - Auto-archive: If neither unsubscribe method is available, QuietBox can archive messages from that sender so they no longer clutter your inbox.
Auto-archiving doesn't stop the emails from arriving. It simply keeps them out of your inbox when an actual unsubscribe option isn't available.
QuietBox also remembers your decisions, so you won't have to review the same senders every week. The Handled tab lets you revisit senders you've chosen to keep and stop archiving messages from senders you've previously selected for that treatment.
There are a few deliberate limits. QuietBox doesn't delete emails, touch your spam folder, or act on a sender without a valid List-Unsubscribe header. It also doesn't unsubscribe anyone automatically based on the model's recommendation.
The model suggests. You decide.
Code
Aravind-Sathesh
/
QuietBox
A Mac menu bar app that unsubscribes you from newsletters you never open, using a local Gemma
QuietBox
A Mac menu bar app that finds the newsletters you never open and unsubscribes you, with one approval a week. Your mail is read and sorted on your Mac by Gemma, an open model running locally through Ollama. No third-party code.
Use it
- Install Ollama and open it.
- Open QuietBox. Setup downloads Gemma if needed, then asks for your email and an app password (Gmail or iCloud). The password goes into your Keychain.
- QuietBox checks your inbox right away, then once a week. When it finds senders you ignore, you get a notification. Untick any you want to keep, then click Unsubscribe.
How it works
- Scan: reads the headers of the last 12 months of INBOX mail over IMAP. Messages stay unread and bodies are never fetched.
- Score: a sender becomes a candidate after 3 or more messages, at least 90% of them unread, none replied to or…
QuietBox is built with Swift 6 and SwiftUI, using Apple's frameworks without third-party package dependencies.
How I Built It
1. Running Gemma Locally
I used Gemma (gemma4:e2b) through Ollama to classify ignored senders as either worth keeping or worth unsubscribing from.
The model runs locally at 127.0.0.1:11434. I also used Ollama's format option to enforce a JSON schema, ensuring every response follows the same structure: { action, reason }.
The action determines the recommendation, while the reason is displayed beneath each sender in the review window.
One design decision was particularly important: the model doesn't decide which emails qualify for analysis, and it never performs an unsubscribe action on its own.
The code handles the initial filtering. It checks the unread ratio, replies, stars, and whether the sender has a usable unsubscribe header. Only senders that pass these checks are sent to Gemma, along with their name, email address, message counts, and five recent subject lines.
Email subjects are treated as untrusted data rather than instructions. The model's response is validated in code, and anything other than a valid "unsubscribe" action defaults to keeping the sender.
I'd rather miss an opportunity to unsubscribe from an unwanted newsletter than risk unsubscribing someone by mistake.
2. Choosing the Right Model
I wasn't sure whether the smaller Gemma model would be sufficient, so I tested two model sizes against six synthetic senders:
- A flash-sale retailer
- A bank
- A shipping tracker
- A daily digest
- A social network notification sender
- An account security alert
Both e2b and 12b classified all six correctly in these tests.
The smaller model took approximately 1.3 seconds per sender, compared with around 3.5 seconds for the larger model.
Since the app already includes a human approval step, the additional processing time didn't seem worthwhile. The smaller model was fast enough for the task, so I chose it for QuietBox.
These tests were a basic sanity check, not a comprehensive evaluation of classification accuracy. Real inboxes contain more ambiguous cases, which is another reason I designed the app to require approval before acting.
3. Reading Email Without a Third-Party Library
Swift doesn't include a built-in IMAP client, and I wanted to avoid adding third-party dependencies just to read email headers. Instead, I implemented the parts of IMAP QuietBox needs using Apple's Network.framework and TLS.
A few implementation details were especially important.
-
Read-only scanning: QuietBox uses
EXAMINE INBOXandBODY.PEEK, so scanning messages doesn't mark unread emails as read. -
Headers only: It fetches the
From,Subject,List-Unsubscribe, andList-Unsubscribe-Postheaders, along with message flags. Message bodies are never downloaded. -
Exact sender matching: IMAP's
SEARCH FROMcan match substrings rather than exact email addresses. For example, searching fora@shop.comcould also matchba@shop.com. Before archiving messages, QuietBox reads theirFromheaders again and verifies the exact sender address. -
Server-aware archiving: Rather than assuming every email provider uses the same archive folder name, QuietBox checks the folder flags advertised by the server to locate the appropriate folder, such as
\Allon Gmail or\Archiveon other servers where supported.
The same networking layer also handles sending unsubscribe emails over SMTP when that option is available.
Building this part was a useful reminder that even a relatively small AI application can involve quite a bit of protocol-level work outside the model itself.
4. Handling Unsubscribe Links Safely
Unsubscribe links come from external email senders, so I didn't want QuietBox to trust them blindly.
I added several checks before using these links:
- One-click unsubscribe URLs must use HTTPS.
-
mailto:links must specify exactly one recipient. - Line breaks are stripped from email subjects to prevent header injection, such as introducing an unintended
Bcc:field. - One-click POST requests use an ephemeral session, so existing cookies aren't sent along.
These measures help reduce the risks involved in processing untrusted email headers. They don't eliminate every possible security risk, but they provide a safer starting point than blindly following whatever link appears in a message header.
5. Building a Mac App People Can Actually Use
My first version was a Python script running on a cron schedule. It handled the basic task, but I wanted something that felt like a proper Mac application rather than a tool that required terminal commands and manual scheduling.
That meant building the experience around the way people actually use their Macs.
The current version includes:
- Guided setup: Checks whether Ollama is running and downloads Gemma with a progress indicator.
- Email account configuration: Supports app-password-based setup, with links to the relevant Gmail or iCloud settings pages.
- Secure credential storage: Stores credentials in the macOS Keychain and checks the connection before saving them.
- Automatic startup: Launches QuietBox when you log in.
- Weekly scheduling: Checks hourly whether a weekly run is due.
- Sleep and wake handling: Checks again when the Mac wakes up, so a missed scheduled run doesn't mean waiting another week.
The goal was to make the app usable without requiring people to understand IMAP, SMTP, Ollama, or local model configuration.
Getting the email handling right, making setup straightforward, and ensuring that the app behaves conservatively were just as important as integrating the AI model.
Why Does Open Innovation Matter?
For QuietBox, using an open model locally isn't just about avoiding API costs. It changes how the application handles something as personal as email.
1. Keeping Email Classification Private
An inbox can reveal a surprising amount about someone's life. I didn't want QuietBox to depend on a third-party AI service receiving email data just to decide which newsletters could be unsubscribed from.
With local inference, Gemma runs on your Mac through Ollama. Email classification doesn't require sending sender details or subject lines to a hosted AI API.
QuietBox still connects to your configured mail server to retrieve messages and perform email operations. When you approve an unsubscribe action, it may also contact an external unsubscribe endpoint or send an unsubscribe email.
The distinction matters: the model runs locally, while the email operations still communicate with the relevant mail services.
2. No Per-Request AI Costs
Unsubscribing from newsletters is a small, repetitive task. Paying for an API every time the app evaluates a sender didn't make much sense to me.
A local model eliminates per-request inference charges. The trade-off is that inference uses your Mac's processing resources, but for a task that runs once a week, local execution seemed like a reasonable choice.
It also means the app doesn't need an ongoing subscription to an AI provider just to keep your inbox manageable.
3. More Control Over the AI
Using an open model makes it easier to experiment with different models and prompts without tying the application's classification logic to a single hosted AI provider.
The decision-making process is also straightforward to inspect:
- Code determines which senders qualify for evaluation.
- Gemma recommends whether to keep or unsubscribe from each sender.
- The app validates the response.
- You review the recommendation and approve the action.
The model's reasoning is visible in the interface, and the final decision remains with the user.
4. Designing for Failure, Not Just Success
Local models aren't perfect, and using an open model doesn't automatically make an application reliable.
I wanted QuietBox to account for that from the beginning.
The app combines deterministic filtering, model-based classification, response validation, and human approval. If the model returns an unexpected response, QuietBox defaults to keeping the sender rather than proceeding with an unsubscribe action.
This approach doesn't guarantee that every recommendation will be correct. It does, however, prevent a model response from directly triggering an unsubscribe without the user's approval.
For an application that interacts with someone's personal inbox, that distinction is important.
What I Learned
Building QuietBox taught me that integrating an AI model is only one part of building a useful AI application.
The more interesting challenges were deciding what the model should handle, what ordinary code should handle, and how to make the overall system safe enough to use with a real inbox.
Gemma is useful for interpreting sender context, something that would be difficult to capture with a long list of hand-written rules alone. Swift handles the filtering, email protocols, validation, and scheduling. The user stays in control of every unsubscribe decision.
I also learned that running a model locally doesn't automatically solve privacy or reliability concerns. You still need to think carefully about network requests, credentials, untrusted input, failure cases, and the consequences of a wrong prediction.
Those considerations shaped QuietBox just as much as the choice of model.
Final Thoughts
QuietBox started with a simple problem: newsletters pile up in your inbox long after you've stopped caring about them.
I wanted to build something that could take care of the tedious part without asking users to give up control of their email or depend on a hosted AI service.
The result is a small Mac app that combines local AI with conventional software engineering. Gemma helps make the classification decisions, Swift handles the email operations, and the user gets the final say.
來源:DEV Community · dev.to

