Open Net Worth
Open Net Worth is an open-source personal AI wealth assistant you run yourself: one ledger for your whole financial life, worked by an AI agent you control.
翻譯準備中
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗A complete, self-hosted personal expense tracker: Python/Flask API + Telegram bot webhook + responsive vanilla HTML/CSS/JavaScript. No frontend build step. Start with RENDER_SETUP_KH.md for the simplest Render + Neon deployment and owner bootstrap.
static/entryui.css + static/entryui.js): big amount with live ៛/$ conversion, Riel/Dollar toggle, quick-add amount chips, category chips, Needs/Wants and Cash/Bank toggles, Today/Yesterday/Pick-date shortcuts, recent-note chips, and a sticky save bar that shows the amount. It drives the same form and API as before, so validation, itemised expenses and favorites are unchanged.static/clean.css).REMINDER_SCHEDULER=1, or an external call to /ops/reminders/<secret> every minute or so)./api/notifications/feed). New Telegram reminders for bills and an optional Sunday weekly summary, both in Settings → Notifications.static/spacious.css): larger gutters, card padding, tap targets and a wider desktop layout./id works before approval; /request submits access; /requests recovers pending requests for the owner.ADMIN_USER_ID grants the owner access; nobody can claim admin through the UI.Scope: This version implements the core tracker. OCR, voice recognition, bank integration and account transfers are not included. The exchange rate is manually editable and used for display totals only; it is not a live market quote. The dashboard net figure is selected-month income minus expenses, not a verified bank balance. Cash/bank are labels only. CSV downloads may depend on Telegram client support; try Telegram Desktop if mobile download is blocked.
@BotFather in Telegram and send /newbot.mymoney-miniapp.py -3.12 -m venv .venv
.venv\Scripts\python -m pip install -r requirements.txt
Copy-Item .env.example .env
.venv\Scripts\python -c "import secrets; print(secrets.token_hex(32))"
python3 -m venv .venv
.venv/bin/python -m pip install -r requirements.txt
cp .env.example .env
.venv/bin/python -c 'import secrets; print(secrets.token_hex(32))'
Put the generated random string in WEBHOOK_SECRET. Edit .env with your own values:
BOT_TOKEN=your_botfather_token
APP_URL=https://your-service.onrender.com
WEBHOOK_SECRET=your_generated_random_secret
ADMIN_USER_ID=your_numeric_personal_telegram_id
# Choose ONE: set a direct password, or use the optional hash below.
ADMIN_PASSWORD=choose_your_own_long_password
# ADMIN_PASSWORD_HASH=your_werkzeug_password_hash
FLASK_SECRET_KEY=your_random_64_character_secret
ALLOWED_USER_IDS=
DATABASE_URL=postgresql://user:password@host/database
Optional extra pre-approved IDs in ALLOWED_USER_IDS are comma-separated. If both ADMIN_USER_ID and ALLOWED_USER_IDS are empty, Mini App access is disabled until owner bootstrap. Approved users are stored in access_requests, not environment variables. Never commit .env or password hashes/secrets.
To choose a password directly, set ADMIN_PASSWORD in Render → Environment to the password you want, then save and redeploy. Keep the Render service private to your trusted users and use a long, unique password. The app compares direct passwords safely and never displays them. For stronger secret handling, you can instead leave ADMIN_PASSWORD empty and set ADMIN_PASSWORD_HASH to a Werkzeug password hash:
.venv\Scripts\python -c "from getpass import getpass; from werkzeug.security import generate_password_hash; print(generate_password_hash(getpass('New Admin password: ')))"
Set FLASK_SECRET_KEY to a separate random secret and keep it unchanged; it signs secure Admin sessions and keyed deletion markers. The password is required again after 15 minutes or when you press Lock. Five failed attempts lock Admin login for 15 minutes. Admin APIs require both valid Telegram Mini App authentication and the password unlock.
app.py should be the service root).?sslmode=require when required.pip install -r requirements.txtpython setup_bot.py && gunicorn wsgi:app --bind 0.0.0.0:$PORT --workers 1 --threads 4 --timeout 60APP_URL to the actual public HTTPS service URL, without a trailing slash. Set PYTHON_VERSION to 3.12.14 if using the included blueprint./health. Deploy. Tables are created automatically for a new database.render.yaml is also provided for Blueprint deployment; fill its prompted values. The blueprint does not provision a database or choose a service plan. Check the provider's current plan pricing, idle/sleep behavior, storage retention and backup policy before choosing it.
Do not use SQLite on an ephemeral host filesystem. Use external PostgreSQL for deployed data. SQLite is only the local default. CSV is an export, not a full database backup (budgets are excluded). Enable database backups and test restoration. The needs/wants feature adds its column to existing transaction tables automatically; other future schema changes may still need migrations because create_all does not migrate existing tables.
The updated Render Start Command runs setup_bot.py automatically. You can skip local setup below when using that command. For first setup, leave ADMIN_USER_ID and ALLOWED_USER_IDS empty, deploy, send /id to the bot, then place your own numeric ID in ADMIN_USER_ID and redeploy. Send /start once as admin so the bot can notify you.
On your computer, set .env to the same BOT_TOKEN, APP_URL and WEBHOOK_SECRET as the deployed service, then run:
.venv\Scripts\python setup_bot.py
Linux/macOS: .venv/bin/python setup_bot.py.
The script registers the webhook, chat menu button and bot commands including /start, /menu, /id, /summary and /allowance. It replaces any existing webhook for this bot; use a dedicated bot, and do not run a polling bot with the same token.
If you don't know your Telegram ID: deploy initially with an empty ALLOWED_USER_IDS, run setup_bot.py, then send /id in your bot's private chat. Copy that numeric ID into Render's ALLOWED_USER_IDS environment variable and redeploy. The /id command works before access is allowed. For approval management, put your personal ID in ADMIN_USER_ID, not only in ALLOWED_USER_IDS.
Now send /start and tap 💰 Open MyMoney or the chat menu button. Use a private bot chat. The app must be opened through Telegram for authenticated API access. Ordinary browser access shows the layout and an instruction to open Telegram; it does not bypass login.
The Mini App checks Render's Deploy API when an external uptime monitor calls /ops/render-deploy-watch/<RENDER_WATCH_SECRET>. It sends one Telegram notice after each deploy reaches a final status (live, failed, or canceled), and stores notice IDs in PostgreSQL to avoid repeats. This uses Render API credentials kept in Render Environment; no GitHub Actions workflow or GitHub Secrets are used.
Add RENDER_API_KEY and a long random RENDER_WATCH_SECRET in Render → Environment. Render automatically provides RENDER_SERVICE_ID. Keep BOT_TOKEN and numeric ADMIN_USER_ID set there, and send /start to the bot as Admin. Configure a separate HTTP monitor (for example, UptimeRobot) to request https://YOUR-APP.onrender.com/ops/render-deploy-watch/YOUR_SECRET every five minutes. Treat the monitor URL as a secret. The endpoint returns a pending status while a deploy is in progress and checks again at the next monitor request.
This is polling, so notices can be delayed up to the monitor interval. It requires the existing service to be reachable when the monitor calls it; if the service is fully unavailable, it cannot notify through itself. Render's direct workspace webhooks can deliver official deploy events, but require a Pro workspace.
Users choose their reminders in Settings → Notifications:
| Reminder | Default | What it does |
|---|---|---|
| Daily spending reminder | Off | At the chosen hour (Phnom Penh time, sent within a 3-hour window) tells the user they haven't logged anything today. Skipped if they already saved a transaction that day. |
| Debt due-date reminders | On | From 09:00, one grouped message listing debts that are due in N days (0–7, user's choice), due today, or overdue by up to 14 days. Each stage is announced once per debt; changing a due date re-arms it. Only open debts with a due date, and only the remaining (unpaid) balance. |
Reminders are sent by the bot, so each user must have started the bot (/start) once. The Send a test message button confirms delivery.
Something has to trigger the check, because the app only runs code when it is awake. Pick one or both (duplicate sends are impossible because every message is recorded in notification_log before it is sent):
REMINDER_SECRET to a long random value (if empty, RENDER_WATCH_SECRET is used) and make an uptime monitor request https://YOUR-APP.onrender.com/ops/reminders/YOUR_SECRET every 5–15 minutes. Treat the URL as a secret. The response is JSON such as {"ok":true,"daily":1,"debt":2,"skipped":0,"failed":0}.REMINDER_SCHEDULER=1; the web process checks every 5 minutes while it is running.If Telegram rejects a message (for example the user never started the bot), the reminder is released and retried on the next check. Reminder history is kept for 90 days.
Dates use Asia/Phnom_Penh. Future transaction dates are rejected. The API accepts month filters from 2000 through 2100. Transactions may have up to 300 note characters. An app session expires after one hour; close and reopen it to refresh Telegram authentication. Authentication data is never stored in localStorage; only the language preference is.
.venv\Scripts\python -m flask --app wsgi run --port 5000
Linux/macOS: .venv/bin/python -m flask --app wsgi run --port 5000.
Use DATABASE_URL=sqlite:///money.db locally. For a real Telegram test, expose the local server through an HTTPS development tunnel, update APP_URL, and run setup_bot.py again. The repository does not include an authentication bypass. No live Telegram token is needed for automated tests.
.venv\Scripts\python -m pip install -r requirements-dev.txt
.venv\Scripts\python -m pytest -q
Tests cover signed authentication, expired/forged sessions, user ownership, create retry deduplication, edit/delete, money precision, currency/month budget isolation, CSV escaping and webhook secret checks. PostgreSQL and live Telegram deployment require verification with your own service configuration.
/id, set the correct numeric allowlist and redeploy.setup_bot.py, reopen the private bot chat, send /start.app.py: API, database schema, Telegram authentication and webhook.wsgi.py: web service entry point.setup_bot.py: one-time Telegram registration.static/: Mini App HTML, CSS and JavaScript.tests/test_app.py: backend regression tests..env.example, render.yaml: configuration templates.@BotFather ហើយរក្សា Token ជាសម្ងាត់។DATABASE_URL។ កុំប្រើ SQLite លើ hosting ដែលលុបទិន្នន័យពេល redeploy។BOT_TOKEN, APP_URL, WEBHOOK_SECRET និង ALLOWED_USER_IDS។.env ហើយ run setup_bot.py។/id ទៅ Bot បន្ទាប់ពី setup រួច។ បន្ថែមលេខនោះក្នុង ALLOWED_USER_IDS ហើយ redeploy។/start ហើយចុចបើក MyMoney។ មិនចាំបាច់វាយ Command ដើម្បីកត់ចំណាយទេ។មិនទាន់ deploy ដោយស្វ័យប្រវត្តិទេ៖ អ្នកត្រូវបំពេញ Token, HTTPS URL និង Database របស់អ្នកជាមុន។
Render Deploy API watcher ផ្ញើស្ថានភាព deploy ផ្លូវការទៅ Telegram Admin ដោយប្រើ RENDER_API_KEY, RENDER_WATCH_SECRET, BOT_TOKEN និង ADMIN_USER_ID នៅក្នុង Render Environment។ បង្កើត HTTP monitor ឱ្យហៅ https://YOUR-APP.onrender.com/ops/render-deploy-watch/YOUR_SECRET រៀងរាល់ 5 នាទី។ កុំដាក់ secret ទាំងនេះក្នុង GitHub។
កំណត់ RENDER_API_KEY និង RENDER_WATCH_SECRET នៅ Render → Environment រួមជាមួយ BOT_TOKEN និង ADMIN_USER_ID ដែលអ្នកប្រើរួច។ Render ផ្ដល់ RENDER_SERVICE_ID ដោយស្វ័យប្រវត្តិ។ Admin ត្រូវផ្ញើ /start ទៅ Bot ម្ដង។ កុំបញ្ចូល secret ណាមួយក្នុង GitHub។
New users send /start and tap Request access, or send /request. The bot displays their own ID. An admin receives their name/ID and Approve/Reject buttons. Decisions are saved in the new access_requests table; existing transaction/budget tables are not altered. A deployment creates this new table automatically.
Only ADMIN_USER_ID may approve or reject. Callback decisions apply only to pending requests; repeated/stale clicks cannot reverse them. Notifications for the same pending user are throttled to one per minute. If Telegram delivery fails, requests remain saved and the admin can use /requests to retrieve batches of five. Decisions remain saved even if a user blocks the notification; the user can reopen the app to check.
Rejected users cannot automatically reapply. The owner can grant or revoke access through /admin with confirmation. Saved decisions override ALLOWED_USER_IDS. Revoking access preserves records. The owner can review users' transactions, budgets and savings goals through the Telegram admin panel and Mini App. Only the configured owner can edit or add a user's transactions or send direct messages; deleted transactions are archived in the user's Trash for restoration. Admin users cannot use these owner-only actions. Users are informed that the owner/admin can review their financial records.
When upgrading an existing deployment, re-register the webhook with allowed_updates including BOTH message and callback_query. The new setup script/Start Command handles this. A setWebhook success does not by itself prove delivery; inspect getWebhookInfo and service logs if /id does not respond.
Upload ALL project files, including admin_panel.py and static/index.html, style.css and app.js. Do not replace only app.js. Keep your existing Neon DATABASE_URL. New tables are created automatically; existing financial records are retained.
Telegram budget warnings run when saving an expense or monthly budget, for the current month in Asia/Phnom_Penh. One alert per threshold (80%, 100%), currency and month; changing the budget does not reset sent alerts. There is no background reminder scheduler. Failed notification delivery does not roll back the saved transaction.
Savings totals are manual records, not bank transfers and do not change income/expenses. The owner must set their own numeric ADMIN_USER_ID in Render; no admin identity is hardcoded.
/allowance shows the same calculation for each currency with a budget. Set a monthly budget first.Home now focuses on monthly totals, the current summary and recent activity. Insights contains the daily bar chart and category donut, including percentages, exact amounts, largest category and empty-state support. Donut values follow the selected month/currency and update after edits/deletions. Desktop uses two columns at 900px and above; mobile remains one column. Budget editing is available through the Home budget card’s Manage button.
History supports Add expense/income, tap-to-view, Edit and individual Delete. Select enables checkboxes; Select visible selects only currently rendered rows. Load more reveals additional rows. Filtering discards selections outside the current filter. Delete selected previews the count before confirmation.
Manage my data → Delete all my entries covers all of your transaction history, across months and both currencies. It preserves budgets, goals and access settings. The dialog requires typing DELETE. Deletion moves records to Trash. Restore from Settings → Trash; only Delete forever inside Trash is permanent. The server uses an owner-bound, five-minute, single-use snapshot, so later entries are not included. Creating a new preview invalidates the old one. No live user records are deleted during deployment.
Upload the full static folder, including design.js, design.css and history.js. The backend creates the delete_batches table automatically. Keep your existing Neon DATABASE_URL.
The eye button in the header hides monetary values in the current browser until toggled back on; the choice is stored locally in that browser. It is a quick privacy aid for shoulder viewing, not a password lock. Up to 12 personal favorites can be saved from an entry form and launched from Home. A favorite fills a draft; review it and press Save to make a transaction. Removing a favorite does not delete transactions.