trash-cleaner
Version:
Finds and deletes trash email in the mailbox
419 lines (312 loc) • 13.4 kB
Markdown
# Trash Cleaner
A program to delete trash emails based on keyword and label filters.
## Prerequisites
[Node.js & npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) (Node 18+)
## Installation
### Via NPM
```bash
npm install -g trash-cleaner
```
### Via GitHub
```
git clone https://github.com/hasankhan/trash-cleaner
cd trash-cleaner
# If you want to try out the development version then 'git checkout dev'
npm install -g
```
## IMAP Configuration (Recommended)
The simplest way to get started. Works with any email provider.
### Option 1: Secure Login (Recommended)
Store credentials securely in your OS keychain (macOS Keychain, Windows Credential Manager, Linux Secret Service):
```bash
trash-cleaner init # Create config directory with sample files
trash-cleaner login # Prompts for IMAP credentials, saves to OS keychain
```
For other services:
```bash
trash-cleaner login -s gmail
trash-cleaner login -s outlook
```
To remove stored credentials:
```bash
trash-cleaner logout # Remove IMAP credentials from keychain
trash-cleaner logout -s gmail
```
### Option 2: File-based Configuration
If your system doesn't have a keychain, credentials fall back to JSON files:
1. Run `trash-cleaner init` to create sample config files
2. Edit `config/imap.credentials.json` with your email settings:
```json
{
"host": "imap.gmail.com",
"port": 993,
"user": "your-email@gmail.com",
"password": "your-app-password"
}
```
### App Passwords
- **Gmail**: [Create an App Password](https://myaccount.google.com/apppasswords) (requires 2FA)
- **Outlook**: Use your regular password or an app password
- **Yahoo**: Account Security → Generate app password
Common IMAP servers:
| Provider | Host | Port |
|----------|------|------|
| Gmail | imap.gmail.com | 993 |
| Outlook/Hotmail | outlook.office365.com | 993 |
| Yahoo | imap.mail.yahoo.com | 993 |
| iCloud | imap.mail.me.com | 993 |
> **Note**: Some features (undo) are only available with the Gmail/Outlook API backends. Use `--service gmail` or `--service outlook` for those.
## Gmail API Configuration (Advanced)
1. Create a [Google Cloud Platform project with the API enabled](https://developers.google.com/workspace/guides/create-project).
2. Create [Authorization credentials for a desktop application](https://developers.google.com/workspace/guides/create-credentials) and download `gmail.credentials.json` file in the `config` directory.
3. Rename `keywords.json.sample` file in the `config` directory to `keywords.json` and update its contents.
## Outlook API Configuration (Advanced)
1. Register an application with the [Microsoft identity platform](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app).
2. Rename `outlook.credentials.json.sample` file in the `config` directory to `outlook.credentials.json` and update its contents.
3. Rename `keywords.json.sample` file in the `config` directory to `keywords.json` and update its contents.
## Get Started
Initialize the config directory with sample files:
```bash
trash-cleaner init [configDirPath]
```
This creates starter `keywords.json`, `imap.credentials.json`, `gmail.credentials.json`, and `outlook.credentials.json` files. Edit them to match your setup (see configuration sections above).
## Configuring Rules
Rules live in `keywords.yaml` in your config directory (`~/.config/trash-cleaner/` by default). The file is a YAML list of rule objects. Each rule tells trash-cleaner which emails to match and what to do with them.
> **Note:** JSON config files (`keywords.json`, etc.) are also supported for backward compatibility. If both exist, YAML takes priority.
### Rule Fields
| Field | Required | Description |
|-------|----------|-------------|
| `value` | Yes | **Keyword rules**: A regex pattern to match. **LLM rules**: A natural language description. |
| `title` | No | A human-readable name for the rule, shown when it matches. Defaults to `value` if omitted. |
| `fields` | No | Comma-separated email fields to search: `from`, `subject`, `snippet`, `body`, or `*` for all. Default: `*` |
| `labels` | No | Comma-separated folder/label names to scope the rule: `inbox`, `spam`, `trash`, `junk email`, or `*` for all. Default: `*` |
| `action` | No | What to do with matches: `delete`, `archive`, or `mark-as-read`. Default: `delete` |
| `type` | No | Rule type: `keyword` (regex, default) or `llm` (external LLM classification). |
| `llm` | For LLM rules | Name of the LLM provider (must match a key in `llm-providers.yaml`). |
### Keyword Rules (Regex)
Keyword rules use regular expressions to match email content. They are fast and precise.
```yaml
- value: casino
fields: "*"
labels: "*"
title: Casino spam
- value: credit|loan
fields: subject
labels: spam, junk email
title: Credit scams
- value: newsletter
fields: subject
labels: inbox
action: archive
title: Newsletters
- value: notification
fields: subject
labels: inbox
action: mark-as-read
```
- **Delete all trash**: `value: "."` with `labels: trash, deleted items` — the `.` regex matches any character.
- **Match multiple words**: `value: lucky|winner|prize` — uses regex `|` (OR) to match any of the words.
- **Match emoji in subject**: `value: "[\\u{1F600}-\\u{1F64F}]"` — uses Unicode character ranges.
- Matching is **case-insensitive** and **diacritic-insensitive** (e.g., "café" matches "cafe").
### LLM Rules (External CLI)
LLM rules invoke an external AI tool (Claude, Copilot, Ollama, etc.) to classify emails by meaning. Write a natural language description of what you want to match — the tool decides if each email fits.
```yaml
- value: marketing or promotional email
labels: "*"
type: llm
llm: claude
action: archive
title: Marketing emails
- value: someone selling me something
labels: inbox
type: llm
llm: claude
```
#### LLM Provider Configuration
Create `llm-providers.yaml` in your config directory (`~/.config/trash-cleaner/`):
```yaml
claude:
command: claude
args: ["--print", "{{prompt}}"]
copilot:
command: copilot-cli
args: ["-p", "{{prompt}}"]
ollama:
command: ollama
args: ["run", "llama3", "{{prompt}}"]
```
Each provider needs:
- `command` — the CLI executable name (must be in your PATH)
- `args` — arguments array with `{{prompt}}` as a placeholder for the rendered prompt
- `prompt` (optional) — custom prompt template; defaults to asking "does this email match?" with true/false response
The `{{prompt}}` placeholder in args is replaced with the full prompt containing the rule description and email content. The tool must respond with "true" or "false".
- LLM rules are slower than keyword rules (network call per email) but can catch things regex can't.
- No data is sent to trash-cleaner servers — classification goes through your configured CLI tool.
- Run `trash-cleaner init` to create a sample `llm-providers.yaml`.
### Scoping Rules with Labels and Fields
Use `labels` to limit which folders a rule applies to:
- `"labels": "inbox"` — only match emails in your inbox
- `"labels": "spam,junk email"` — only match emails in spam/junk folders
- `"labels": "trash,deleted items"` — only match emails in trash
- `"labels": "*"` — match emails in any folder
Use `fields` to limit which parts of an email are searched (keyword rules only):
- `"fields": "subject"` — only search the subject line
- `"fields": "subject,body"` — search subject and body
- `"fields": "*"` — search all fields (from, subject, snippet, body)
### Example Configuration
```yaml
- value: "."
fields: "*"
labels: trash, deleted items
title: Clean trash folder
- value: casino|lottery|winner
fields: "*"
labels: spam, junk email
title: Gambling spam
- value: unsubscribe
fields: body
labels: inbox
action: archive
title: Bulk mail
- value: notification
fields: subject
labels: inbox
action: mark-as-read
- value: marketing or promotional email
labels: inbox
type: llm
llm: claude
action: archive
title: Marketing emails
```
Rules are evaluated in order — the **first matching rule wins**. Place more specific rules before general ones.
To get the list of all parameters type `trash-cleaner -h`
```
Usage: trash-cleaner [options]
Options:
-V, --version output the version number
-r, --reconfig reconfigures the auth for a service
-t, --dry-run perform a dry-run cleanup without deleting the emails
-d, --debug output extra debugging info
-l, --launch launch the auth url in the browser
-q, --quiet suppress verbose output (for cron/scripts)
-i, --interactive preview matches and confirm before acting
-f, --format <format> output format: text or html (default: "text")
-m, --min-age <days> only process emails older than N days
-c, --configDirPath <path> the path to config directory (default: "config")
-s, --service <service> the email service to use (choices: "imap", "gmail", "outlook", default: "imap")
-a, --account <name> the account name for multi-account support (default: "default")
-h, --help display help for command
```
## Commands
### `trash-cleaner init [configDirPath]`
Creates a config directory with sample configuration files.
### `trash-cleaner list-rules [configDirPath]`
Displays all active keyword rules and allowlist patterns.
### `trash-cleaner validate [configDirPath]`
Validates configuration files and reports any issues.
### `trash-cleaner undo [configDirPath]`
Shows the last batch of processed emails and offers to restore them.
### `trash-cleaner login [-s service] [-a account]`
Prompts for credentials and saves them securely in the OS keychain. Supports `--service imap` (default), `gmail`, or `outlook`.
### `trash-cleaner logout [-s service] [-a account]`
Removes stored credentials from the OS keychain.
## Features
### Multi-Account Support
Run against different accounts using the `-a` flag:
```bash
trash-cleaner -s gmail -a work
trash-cleaner -s gmail -a personal
```
Each account stores its own credentials (e.g., `gmail.credentials.work.json`).
### Sender Allowlist
Create `config/allowlist.json` to protect specific senders from any actions:
```json
[
"boss@company\\.com",
".*@important\\.org"
]
```
Patterns are case-insensitive regular expressions matched against the sender.
### Interactive Mode
Use `--interactive` to preview matched emails before taking action:
```bash
trash-cleaner --interactive
```
### Quiet Mode
Use `--quiet` for cron jobs or scripts — suppresses spinner and verbose output:
```bash
trash-cleaner --quiet
```
### HTML Reports
Generate an HTML report instead of console output:
```bash
trash-cleaner --format html
```
This creates a timestamped HTML file in the current directory.
### Retry Logic
API calls automatically retry with exponential backoff on transient failures (429, 5xx, network errors).
### Email Age Filter
Only process emails older than a certain number of days:
```bash
trash-cleaner --min-age 7
```
### Config Validation
Check your configuration files for errors before running:
```bash
trash-cleaner validate
```
### Undo
After processing, actions are logged. Use `trash-cleaner undo` to restore the last batch:
```bash
trash-cleaner undo
```
## Scheduling
To run trash-cleaner automatically on a schedule:
### Linux/macOS (cron)
Run `crontab -e` and add a line. For example, to run every hour:
```
0 * * * * /usr/local/bin/trash-cleaner --quiet -c /path/to/config
```
### macOS (launchd)
Create `~/Library/LaunchAgents/com.trash-cleaner.plist`:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.trash-cleaner</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/trash-cleaner</string>
<string>--quiet</string>
<string>-c</string>
<string>/path/to/config</string>
</array>
<key>StartInterval</key>
<integer>3600</integer>
</dict>
</plist>
```
Load it with: `launchctl load ~/Library/LaunchAgents/com.trash-cleaner.plist`
### Windows (Task Scheduler)
```powershell
schtasks /create /tn "TrashCleaner" /tr "trash-cleaner --quiet -c C:\path\to\config" /sc hourly
```
## Development
```bash
npm install # Install dependencies
npm test # Run tests
npm run lint # Run ESLint
npm run typecheck # Run JSDoc type checking
npm run coverage # Generate coverage report
```
## Releasing
Releases are automated via GitHub Actions. To publish a new version:
```bash
npm version patch # or minor, or major
git push --follow-tags
```
This triggers the publish workflow which runs tests and publishes to npm.
Requires `NPM_TOKEN` secret configured in the repository settings.