Skip to content

amarangganar/pixance

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

27 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Pixance

Your financial fairy 🧚

A personal finance Telegram bot that logs cashflow via natural language and stores everything in Google Sheets.


Features

  • Natural language input β€” just message the bot like you're texting a friend
  • Bilingual β€” auto-detects Indonesian and English per message
  • Backdate support β€” log past transactions with relative ("kemarin") or absolute ("30 maret") dates
  • Pocket management β€” categorize money across accounts, wallets, or e-wallets
  • Google Sheets as dashboard β€” your data lives in a spreadsheet you can see, edit, and export
  • No balance tracking β€” cashflow-first: income, expenses, and transfers only

Prerequisites


Setup

1. Clone & install

git clone https://github.com/your-username/pixance.git
cd pixance
bun install
cp .env.example .env

2. Environment variables

Fill in .env:

Variable Required Description
BOT_TOKEN βœ… Telegram bot token from @BotFather
OWNER_CHAT_ID βœ… Your Telegram user ID β€” get it from @userinfobot
AI_GATEWAY_API_KEY βœ… Vercel AI Gateway API key
AI_MODEL β€” Model to use (default: anthropic/claude-sonnet-4-5)
GOOGLE_SERVICE_ACCOUNT_JSON βœ… Full service account JSON as a string (see below)
SPREADSHEET_ID βœ… Google Sheets ID from the URL (see below)
WEBHOOK_URL βœ… Public HTTPS URL Telegram will POST updates to
PORT β€” Server port (default: 3000)

OWNER_CHAT_ID locks the bot to a single user. To find your ID, message @userinfobot β€” it replies with your numeric user ID.

3. Google Sheets setup

Two separate things to get. Here's exactly how.


Spreadsheet ID

The easy one. Create a new Google Sheet, then grab the ID from the URL:

https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms/edit
                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                                       this is your SPREADSHEET_ID

Copy the string between /d/ and /edit. That's it.


Google Service Account JSON

This is the bot's identity β€” how it authenticates to the Sheets API without a browser login.

Step 1 β€” Create a Google Cloud project

  1. Go to console.cloud.google.com
  2. Click the project dropdown (top left) β†’ New Project
  3. Name it anything, e.g. finance-bot β†’ Create

Step 2 β€” Enable the Sheets API

  1. With your project selected, go to APIs & Services β†’ Library
  2. Search "Google Sheets API" β†’ click it β†’ Enable

Step 3 β€” Create a service account

  1. Go to APIs & Services β†’ Credentials
  2. Click + Create Credentials β†’ Service Account
  3. Name it anything, e.g. finance-bot-sa β†’ Create and Continue
  4. Skip the optional role and user access steps β†’ Done

Step 4 β€” Generate the JSON key

  1. Click your new service account from the credentials list
  2. Go to the Keys tab
  3. Add Key β†’ Create new key β†’ JSON β†’ Create
  4. A .json file downloads automatically β€” this is your GOOGLE_SERVICE_ACCOUNT_JSON

The file looks like:

{
  "type": "service_account",
  "project_id": "finance-bot-xxxxx",
  "private_key_id": "abc123...",
  "private_key": "-----BEGIN RSA PRIVATE KEY-----\n...",
  "client_email": "finance-bot-sa@finance-bot-xxxxx.iam.gserviceaccount.com",
  "client_id": "...",
  ...
}

Step 5 β€” Share the spreadsheet with the service account

This is the step people most often forget.

The service account is like a separate Google user β€” it needs explicit access to your sheet.

  1. Open your Google Sheet
  2. Click Share (top right)
  3. Paste the client_email from the JSON file
  4. Set permission to Editor
  5. Send (ignore the "can't notify" warning if it appears)

Step 6 β€” Put it in your .env

The entire JSON content goes in as a single-line string:

# Option A β€” paste the whole JSON as a string (wrap in single quotes)
GOOGLE_SERVICE_ACCOUNT_JSON='{"type":"service_account","project_id":"..."}'

# Option B β€” on Railway/cloud platforms, paste the raw JSON directly into the env var field
# Most deployment platforms handle multiline values fine in their UI

Quick checklist

βœ… Google Cloud project created
βœ… Google Sheets API enabled
βœ… Service account created
βœ… JSON key downloaded
βœ… Spreadsheet created
βœ… Spreadsheet shared with service account email (Editor)
βœ… SPREADSHEET_ID copied from URL
βœ… GOOGLE_SERVICE_ACCOUNT_JSON set in .env

If the bot throws a 403 error on first run, the missing share in Step 5 is almost always why.


4. Local development webhook

Telegram requires a public HTTPS URL to deliver updates. Use cloudflared β€” no account needed.

# Install (macOS)
brew install cloudflare/cloudflare/cloudflared

# Expose your local port
cloudflared tunnel --url http://localhost:3000

Copy the printed *.trycloudflare.com URL and set it as WEBHOOK_URL in .env.

Note: The URL changes every time you run cloudflared. Update WEBHOOK_URL and restart the bot each dev session.

5. Run

bun dev    # with hot reload
bun start  # without hot reload

Usage

Slash commands

Command Description
/start Show help and active pockets
/report [today|week|month] [lang] Financial summary for the given period (lang: id or en, default: today)
/history [lang] Last 10 transactions
/pockets List active pockets with balances and total
/pockets all List all pockets including archived
/addpocket <name> Create a new pocket
/renamepocket <old> <new> Rename a pocket (use quotes for names with spaces)
/archivepocket <name> Archive a pocket
/restorepocket <name> Restore an archived pocket
/delete <n> Delete the nth most recent transaction

Natural language

Just send a message β€” the bot figures out the rest:

kopi 25rb pake gopay              β†’ expense: Food & Drinks, 25k, Gopay
gajian 8jt ke BCA                 β†’ income: Salary, 8M, BCA
transfer BCA ke Gopay 1jt         β†’ transfer: 1M from BCA to Gopay
kasih uang 200rb ke Budi          β†’ expense: Gift Given, 200k
pinjemin 500rb ke Andi            β†’ expense: Loan Given, 500k
kopi 25rb kemarin                 β†’ expense logged with yesterday's date
makan siang 50rb 2 hari lalu      β†’ expense logged 2 days ago
beli bensin 30 maret              β†’ expense logged on March 30
hapus kopi 25rb tadi              β†’ finds and deletes matching transaction

Deployment

# Build and start
docker compose up -d --build

# Update after git pull
git pull && docker compose up -d --build

# View logs
docker compose logs -f

# Stop
docker compose down

Development

bun test      # run tests
bun lint      # lint with Biome
bun format    # format with Biome
bun check     # lint + format + fix

About

Your personal financial fairy 🧚

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors