add readme & docstrings
This commit is contained in:
parent
336b732c60
commit
b3cfb961d2
87
README.md
Normal file
87
README.md
Normal file
@ -0,0 +1,87 @@
|
||||
# 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 <alerts@minimalbank.com>" and "Transaction Alert" in msg["Subject"]
|
||||
|
||||
def extract(self, msg):
|
||||
body = msg.get_body().as_string()
|
||||
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
|
||||
```
|
7
model.py
7
model.py
@ -6,9 +6,14 @@ from uuid import UUID
|
||||
|
||||
@dataclass
|
||||
class Transaction:
|
||||
"""Represents an Actual Budget transaction to be submitted.
|
||||
|
||||
See: https://actualbudget.org/docs/api/reference#transaction for field descriptions
|
||||
"""
|
||||
|
||||
account: UUID
|
||||
date: date
|
||||
amount: Decimal
|
||||
amount: Decimal # Note: decimal dollars, JS shim will convert to cents as described in the API
|
||||
payee: str # imported_payee in API
|
||||
notes: str
|
||||
imported_id: str
|
||||
|
21
parsers.py
21
parsers.py
@ -13,10 +13,31 @@ from model import Transaction
|
||||
class TransactionParser(ABC):
|
||||
@abstractmethod
|
||||
def match(self, msg: Message) -> bool:
|
||||
"""
|
||||
Determines if the given email message matches the criteria for this parser.
|
||||
|
||||
Args:
|
||||
msg (Message): The email message to evaluate.
|
||||
|
||||
Returns:
|
||||
bool: True if the message matches the parser's criteria, False otherwise.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def extract(self, msg: EmailMessage) -> Transaction:
|
||||
"""
|
||||
Extracts transaction details from the given email message.
|
||||
|
||||
Args:
|
||||
msg (EmailMessage): The email message to parse.
|
||||
|
||||
Returns:
|
||||
Transaction: A Transaction object containing the extracted details.
|
||||
|
||||
Raises:
|
||||
TransactionParsingFailed: If the message cannot be parsed successfully.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user