commit 5fb519446871c5dc4de4a42b79764d1d54502bbd Author: julle Date: Fri Apr 24 18:44:10 2026 +0200 first commit diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..8756118 --- /dev/null +++ b/.env.example @@ -0,0 +1,9 @@ +# Enable Banking Credentials +EB_CLIENT_ID=your_client_id +EB_CLIENT_SECRET=your_client_secret + +# Actual Budget Configuration +ACTUAL_SERVER_URL=https://your-actual-server.com +ACTUAL_PASSWORD=your_server_password +ACTUAL_BUDGET_ID=your_sync_id +ACTUAL_ENCRYPTION_PASSWORD=your_optional_encryption_pwd diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ac973e5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,155 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script, and contain the bin/ +# and lib/ directories that PyInstaller runs from +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.node_repl_history +.pytest_cache/ +pytestdebug.log + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the Python version +# is generally handled by the user. In contrast, a Python application should check them in. +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or even +# fail all-together, on other platforms. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock + +# PEP 582; used by e.g. github.com/frenzymadness/venereal or github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +.idea/ + +# VS Code +.vscode/ + +# macOS +.DS_Store diff --git a/README.md b/README.md new file mode 100644 index 0000000..93d87ed --- /dev/null +++ b/README.md @@ -0,0 +1,77 @@ +# Actualize 💸 + +**Actualize** is a lightweight Python synchronization bridge that connects your bank accounts (via [Enable Banking](https://enablebanking.com/)) directly to your [Actual Budget](https://actualbudget.org/) instance. + +Automate your financial tracking without sacrificing privacy. Actualize pulls your real-time bank data and pushes it directly into Actual Budget, eliminating manual CSV exports forever. + +## ✨ Features + +- **Python-Powered:** Built with `actualpy` and `enablebanking_sdk` for maximum flexibility. +- **Automated Sync:** Fetch transactions from hundreds of banks via Enable Banking's Open Banking API. +- **Privacy First:** Local-first architecture. Your credentials and transaction data stay on your machine. +- **Smart Deduplication:** Uses Actual Budget's internal reconciliation logic to prevent double entries. +- **Headless & Scalable:** Perfect for running on a Raspberry Pi, home server, or as a scheduled GitHub Action. + +## 🚀 Getting Started + +### Prerequisites + +- **Python 3.9+** +- An [Enable Banking](https://enablebanking.com/) developer account and API keys. +- A running instance of [Actual Budget](https://actualbudget.org/). + +### Installation + +1. **Clone the repository:** + ```bash + git clone https://github.com/yourusername/actualize.git + cd actualize + ``` + +2. **Create a virtual environment & install dependencies:** + ```bash + python -m venv venv + source venv/bin/activate # On Windows: venv\Scripts\activate + pip install actualpy enablebanking_sdk python-dotenv + ``` + +3. **Configure environment variables:** + Create a `.env` file in the root directory: + ```env + # Enable Banking Credentials + EB_CLIENT_ID=your_client_id + EB_CLIENT_SECRET=your_client_secret + + # Actual Budget Configuration + ACTUAL_SERVER_URL=https://your-actual-server.com + ACTUAL_PASSWORD=your_server_password + ACTUAL_BUDGET_ID=your_sync_id + ACTUAL_ENCRYPTION_PASSWORD=your_optional_encryption_pwd + ``` + +## 🛠 Usage + +1. **Authentication:** + Run the initial auth script to link your bank accounts and generate tokens. + ```bash + python authenticate.py + ``` + +2. **Sync:** + Run the sync script to pull transactions and push them to Actual. + ```bash + python sync.py + ``` + +## 📦 Tech Stack + +- **Actual Budget API:** [actualpy](https://github.com/bvanelli/actualpy) +- **Bank Integration:** [enablebanking-python-sdk](https://github.com/nocfo/enablebanking-python-sdk) +- **Environment Management:** [python-dotenv](https://github.com/theskumar/python-dotenv) + +## 📄 License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +--- +*Disclaimer: This project is not officially affiliated with Actual Budget or Enable Banking.* \ No newline at end of file diff --git a/authenticate.py b/authenticate.py new file mode 100644 index 0000000..ea053d0 --- /dev/null +++ b/authenticate.py @@ -0,0 +1,10 @@ +from src.bank_client import BankClient + +def main(): + print("Starting bank authentication process...") + client = BankClient() + client.authenticate() + print("Authentication complete.") + +if __name__ == "__main__": + main() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..b4b4753 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +actualpy +enablebanking_sdk +python-dotenv diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..8f8184d --- /dev/null +++ b/src/__init__.py @@ -0,0 +1 @@ +# Actualize source package diff --git a/src/actual_client.py b/src/actual_client.py new file mode 100644 index 0000000..d9e9d75 --- /dev/null +++ b/src/actual_client.py @@ -0,0 +1,19 @@ +from actual import Actual +from .config import Config + +class ActualClient: + def __init__(self): + self.actual = Actual( + base_url=Config.ACTUAL_SERVER_URL, + password=Config.ACTUAL_PASSWORD, + budget_id=Config.ACTUAL_BUDGET_ID, + encryption_password=Config.ACTUAL_ENCRYPTION_PASSWORD + ) + + def connect(self): + # Placeholder for connection logic + pass + + def push_transactions(self, transactions): + # Placeholder for pushing transactions to Actual Budget + pass diff --git a/src/bank_client.py b/src/bank_client.py new file mode 100644 index 0000000..48ac9c2 --- /dev/null +++ b/src/bank_client.py @@ -0,0 +1,17 @@ +import enablebanking +from .config import Config + +class BankClient: + def __init__(self): + self.api_client = enablebanking.ApiClient( + client_id=Config.EB_CLIENT_ID, + client_secret=Config.EB_CLIENT_SECRET + ) + + def authenticate(self): + # Placeholder for bank authentication logic + pass + + def fetch_transactions(self, account_id, start_date): + # Placeholder for fetching transactions from bank + pass diff --git a/src/config.py b/src/config.py new file mode 100644 index 0000000..f7536a0 --- /dev/null +++ b/src/config.py @@ -0,0 +1,13 @@ +import os +from dotenv import load_dotenv + +load_dotenv() + +class Config: + EB_CLIENT_ID = os.getenv("EB_CLIENT_ID") + EB_CLIENT_SECRET = os.getenv("EB_CLIENT_SECRET") + + ACTUAL_SERVER_URL = os.getenv("ACTUAL_SERVER_URL") + ACTUAL_PASSWORD = os.getenv("ACTUAL_PASSWORD") + ACTUAL_BUDGET_ID = os.getenv("ACTUAL_BUDGET_ID") + ACTUAL_ENCRYPTION_PASSWORD = os.getenv("ACTUAL_ENCRYPTION_PASSWORD") diff --git a/sync.py b/sync.py new file mode 100644 index 0000000..1a0912a --- /dev/null +++ b/sync.py @@ -0,0 +1,13 @@ +from src.bank_client import BankClient +from src.actual_client import ActualClient + +def main(): + print("Starting synchronization...") + bank = BankClient() + actual = ActualClient() + + # Logic to fetch from bank and push to actual + print("Sync complete.") + +if __name__ == "__main__": + main() diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_basic.py b/tests/test_basic.py new file mode 100644 index 0000000..3ada1ee --- /dev/null +++ b/tests/test_basic.py @@ -0,0 +1,2 @@ +def test_placeholder(): + assert True