# Actual IMAP Poll This project is designed to poll an IMAP server, looking for transaction notification emails which are pushed to an Actual Budget instance. ## Docker Usage To run this project using Docker, you need to configure the following environment variables: ### Environment Variables | Variable Name | Description | Example Value | | ----------------- | --------------------------------------------------------------------------------------- | -------------------------------------- | | `IMAP_SERVER` | The hostname or IP address of the IMAP server. | `imap.example.com` | | `IMAP_PORT` | The port number for the IMAP server. | `143` | | `IMAP_USER` | The username for authenticating with the IMAP server. | `user@example.com` | | `IMAP_PASS` | The password for authenticating with the IMAP server. | `yourpassword` | | `IMAP_STARTTLS` | Whether to use TLS for the IMAP connection (`true` or `false`). | `true` | | `IMAP_INTERVAL` | The interval (in seconds) between polling the IMAP server. | `300` | | `ACTUAL_SERVER` | The URL to the Actual Budget instance to submit transactions to | `https://demo.actualbudget.org` | | `ACTUAL_PASSWORD` | The password for the Actual Budget instance | `password` | | `ACTUAL_SYNC_ID` | The 'sync ID' UUID of the Actual Budget budget on the server (see Settings / Advanced) | `145b1875-8d2c-4eac-aa2c-b3fd937c6a6d` | | `CONFIG_PATH` | Path to a Python module containing a description of the parsers to execute (see below). | `/data/config.py` | ## Parser Definition The parsers to execute and their parameters are defined in a Python source file which should be mounted to the container at the location specified by `CONFIG_PATH` (e.g. `/data/config.py`). This file should define a list called `PARSERS` which holds a list of `TransactionParser` instances. Since it is Python source, it can also contain custom `TransactionParser` instances. Each parser instance will generally require the account UUID to link the transactions to, which can be obtained from the end of the URL while viewing the account in Actual. ### Example config.py ```python config.py from parsers import RogersBankParser, MBNAParser from uuid import UUID PARSERS = [ RogersBankParser(UUID("ae662c3c-3f36-4aee-a558-aa32ff7b75c8")), MBNAParser(UUID("82a382e6-ff27-49af-841f-0cbddee9fe28")), ] ``` ## Custom Parser Implementation Parsers must implement the abstract class `TransactionParser` defined in `parsers.py`. This class requires two methods: 1. `match(self, msg: Message) -> bool` accepts a `Message/EmailMessage` object and uses it to determine if this parser is responsible for the message and it contains an interesting transaction 2. `extract(self, msg: EmailMessage) -> Transaction` accepts an `EmailMessage` object and extracts a `Transaction` dataclass object for submission to Actual ### Example custom parser Below is a minimal example of a custom parser for a fictional bank, "Minimal Bank." This parser matches emails from "Minimal Bank" and extracts transaction details. It should appear in the `config.py` alongside the `PARSERS` definition. ```python class MinimalBankParser(TransactionParser): def __init__(self, account_id): self.account_id = account_id # Actual account UUID to submit the matching transactions to def match(self, msg): return msg["From"] == "Minimal Bank " and "Transaction Alert" in msg["Subject"] def extract(self, msg): body = msg.get_body().get_content() amount = ... # Extract amount from body date = ... # Extract date from body payee = ... # Extract payee from body return Transaction( account=self.account_id, date=date, amount=amount, payee=payee, notes="via Minimal Bank email", imported_id=msg["Message-ID"], ) ``` ### Example Docker Command ```bash docker run -d \ -e IMAP_SERVER=imap.example.com \ -e IMAP_USER=user@example.com \ -e IMAP_PASS=yourpassword \ -e ACTUAL_SERVER=https://demo.actualbudget.org \ -e ACTUAL_PASSWORD=password \ -e ACTUAL_SYNC_ID=145b1875-8d2c-4eac-aa2c-b3fd937c6a6d \ -v /path/to/config.py:/data/config.py \ actual-imap-poll:latest ```