A standalone Python application that:
- Authenticates with the Gmail API (OAuth2, no IMAP).
- Fetches emails into a PostgreSQL database.
- Applies JSON-configured rules to perform actions (mark read/unread, move to label) using Gmail REST API.
- Simplifies email management with easy-to-follow steps.
- Programming Language: Python 3.10+
- Database: PostgreSQL
- Libraries: SQLAlchemy ORM, Google Gmail API (google-api-python-client), Typer CLI, dotenv
- Package Management:
uv
- Python 3.10 or higher
- PostgreSQL installed and running
- Google Cloud project with Gmail API enabled
uvpackage manager installed globally
-
Clone the Repository:
git clone <repository-url> cd mail-helper
-
Install Dependencies:
uv sync
-
Set Up Environment Variables:
- Copy the example
.envfile:cp .env.bak .env
- Edit
.envto configureDATABASE_URL, Gmail user ID, and other settings.
- Copy the example
-
Create the Database:
createdb mailhelper # Or use psql/GUI; update .env accordingly -
Authenticate with Gmail:
- Follow Google OAuth Documentation to obtain OAuth credentials.
- Save the credentials as
./credentials.json. - Initialize the database and authenticate:
uv run python -m app.cli init-db uv run python -m app.cli auth
- A browser window will open to grant access. The token will be saved to the path specified in
.env(default:./token.json).
Fetch emails from Gmail and store them in the database:
uv run python -m app.cli fetch --max-results 100Define rules in the rules/rules.json file. An example is provided in the file.
Apply the configured rules to emails:
uv run python -m app.cli process --rules-path rules/rules.jsonExecute the test suite:
uv run pytest -qThe rules.json file allows you to define multiple rules to process emails. Below is an example format:
You can define multiple rules in the rules.json file. Each rule can have its own set of conditions and actions. For example:
[
{
"predicate": "All",
"rules": [
{
"field": "From",
"predicate": "Contains",
"value": "example@domain.com"
},
{ "field": "Subject", "predicate": "Contains", "value": "Invoice" }
],
"actions": [{ "type": "mark_as_read" }]
},
{
"predicate": "Any",
"rules": [
{ "field": "From", "predicate": "Contains", "value": "newsletter@" },
{
"field": "Subject",
"predicate": "Does not Contain",
"value": "Important"
}
],
"actions": [
{ "type": "mark_as_unread" },
{ "type": "move_message", "label": "Promotions" }
]
}
]- Fields:
From,To,Subject,Message,Received - String Predicates:
Contains,DoesNotContain,Equals,DoesNotEqual - Date Predicates (on
Received):LessThanDays,GreaterThanDays,LessThanMonths,GreaterThanMonths - Actions:
mark_as_read,mark_as_unread,move_message(requires"label"; falls back toDEFAULT_MOVE_LABELif missing)
- "Move" in Gmail means applying a label and (optionally) removing
INBOX. This app adds the target label and removesINBOXto emulate moving. - The app updates the database
is_readandlabelsafter actions to maintain synchronization. - This is a CLI app; no server is run.
For queries or issues, contact Dharmendra.
[ { "predicate": "All", "rules": [ { "field": "From", "predicate": "Contains", "value": "noreply@" }, { "field": "Subject", "predicate": "DoesNotContain", "value": "Promotion" } ], "actions": [{ "type": "mark_as_read" }] } ]