Booji:在 Android 手機上讀取銀行 SMS、整理訂閲費用並預估每月支出
Booji: the ghost who reads my bank SMS, buries my zombie subscriptions, and tells me what next month will cost
Booji 是一款在 Android 手機上運作的開源記帳應用,讀取銀行發送的 SMS,分類交易、標示重複收費,並預估本月支出及餘額。它以 Gemma 4 E2B 在裝置上處理無法由規則解析的訊息及商戶分類,毋須後端或帳戶。應用以 Kotlin 和 Jetpack Compose 開發,採用 MIT 授權。
This is a submission for the Hacktoberfest Open-Source AI Challenge: Week 4, theme: Ghost in the Machine.
What I Built
The ghost of a chartered accountant, your bank SMS, and nothing leaving the phone.
I've always believed that if you believe you have enough money, the universe will find a way to give you that money.
Delusional, right?
Well, the ghost living in my house thinks so too.
He keeps watching me go out every weekend, browse and order things online, and on the rare days when I'm actually home? Order in.
I think this has given him anxiety. And he's doing something to me. :/
I've started noticing that all my transactions have been categorised for me. The subscriptions I'd forgotten I was paying for have been highlighted. And now there are notes on my phone telling me how to plan for next month?!
That's it. I'm trapping him on my phone.
He wants to make a ledger so badly? He's going to have to do it for the rest of his afterlife.
Meet Booji.
Booji is a ghost accountant who lives on your Android phone. He reads the SMS messages your bank already sends after every debit and credit, and takes you on three rather uncomfortable visits:
The Ghost of Spending Past: Booji digs through your transactions and categorises them into jars, one for each kind of spending. Each jar fills up according to its share of your expenses. Tap one to see exactly what went into it, which merchants kept coming back, and where your money went. Every transaction gets a category that you can correct with a tap.
The Graveyard — The Ghost of Spending Present: Not every haunting belongs to the past. Some charges are very much alive, returning month after month whether you remember them or not. The Graveyard is where Booji lays them to rest. Every recurring charge gets a headstone bearing its name and monthly cost. You decide whether it is alive (something you still use) or a zombie (a subscription you've long forgotten but are still paying for). Booji keeps a running tally of what these financial zombies are costing you.
The Future — The Ghost of Spending Yet to Come: Finally, Booji peers into a crystal ball. It looks at your spending patterns, upcoming recurring charges, income and even the Indian festival calendar to estimate what the rest of your month might look like. It shows you what the month is likely to cost, the range of possible outcomes, and where your balance might land. If a festival is approaching, it sets aside an allowance rather than pretending the next few weeks will look like an ordinary month. It also shows you what's eating into your money and what you could save by making a few changes.
The idea isn't to frighten you into spending less. It's to give you enough clarity to spend without constantly worrying about what your bank balance will look like tomorrow.
Booji wants you to have money left over, and the freedom to say yes to the occasional nice thing without having to check your balance first.
After all, even a ghost needs a purpose. Booji just happens to have found one in your finances.
Why A Christmas Carol, beyond the costumes?
Because Dickens wrote a ghost story about money, and I seem to have found my own version of it in the average Indian bank statement. There are the ghosts of purchases past, the subscriptions that refuse to die, and the rather uncomfortable possibility of what happens if we keep going the way we are.
Booji simply makes those ghosts visible. And, much like Scrooge, you get a chance to do something about them.
Demo
| Past | Graveyard | Future |
|---|---|---|
![]() |
![]() |
![]() |
| The festival allowance | What is eating the month |
|---|---|
![]() |
![]() |
| SMS the rules could not read | One of them, read by Booji | Booji reviews the month |
|---|---|---|
![]() |
![]() |
![]() |
| Booji reads the headstones | Booji looks ahead | Inside a jar |
|---|---|---|
![]() |
![]() |
![]() |
Judges: the APK is available on the releases page.
Switch on Demo inbox in Settings and Booji will populate itself with three months of synthetic HDFC-format bank SMS, processed through exactly the same parser used for real messages. No Indian bank account required.
With real SMS, Booji tracks every account that sends a balance notification and combines them. I currently have three accounts in mine.
Code
github.com/ShalinaBhargava/booji
MIT licensed. Kotlin, Jetpack Compose, no backend, no accounts.
How I Built It
A ghost accountant sounds like a fairly specialised job. Fortunately, I had Gemma.
Giving the ghost a brain: the open-weight model.
Booji runs Gemma 4 E2B directly on the phone through LiteRT-LM, Google's on-device inference runtime. The weights are the Apache 2.0 release on Hugging Face, weighing in at 2.6 GB. They're downloaded once from inside the app over Wi-Fi (or can be pushed using adb). Inference runs on the GPU first, with the CPU as a fallback.
Gemma has three responsibilities. Two of them are particularly important because they are things a regular expression simply cannot do reliably.
- Reading the messages the rules cannot.
Three banks and a card provider each have their own peculiar way of describing a transaction. The regex parser handles the formats it knows, but anything it doesn't recognise used to be discarded.
Now, if a bank-looking SMS contains a rupee figure but cannot be parsed, Booji sets it aside for review. One tap sends up to six messages to Gemma, which extracts the amount, direction, merchant, account, balance and card limit as JSON.
Each message is read only once. The result is saved as a transaction marked read by Booji, with an option to discard it if the model gets something wrong.
On my own inbox, this made a considerable difference. Instead of being limited to the four bank formats my parser understands, Booji can now make sense of the messages that fall outside those rules.
- Making sense of merchants.
Some merchants are easy to identify. Swiggy is food, Netflix is a subscription, and anything containing the word "rent" is probably rent. Keyword rules handle these obvious cases.
The rest go to Gemma in batches. It receives a list of merchant names and returns their categories along with cleaned-up names. So "Blinkit Commerc" and "Blinkit" become the same merchant across the jars, the graveyard and the recurring transaction history.
There is an additional safeguard: a cleaned-up name is only accepted if it visibly shares its beginning with the original. This prevents something like "Mom" from accidentally being merged into "Transfer".
And, importantly, your corrections always take precedence. Once you correct a merchant, Booji remembers that preference for future transactions.
- Giving the ghosts a voice.
Each tab has a Summon button. Press it, and Gemma receives a small JSON containing the numbers Booji has already calculated: monthly spending, top categories, recurring charges, zombie costs, forecast ranges, remaining money against the monthly goal, and the biggest drains on the budget.
It then turns those facts into three short paragraphs in character.
The model isn't asked to analyse your finances from scratch. It is asked to interpret the numbers and explain what they mean, in the voice of a rather opinionated dead accountant.
The architecture: a haunted house with no landlord.
Booji is a single Android application with no backend. Everything that happens before the model is deterministic Kotlin. The model only sees finished facts, and only when you explicitly ask it to do something.
flowchart TB
SMS[(Bank SMS inbox)] --> Reader[SmsReader<br/>keeps bank-looking senders]
Reader --> Parser[BankParser<br/>regex: amount, direction, merchant,<br/>account, bank, balance, card limit]
Parser -->|parsed| Mark
Parser -->|unreadable, has a rupee figure| Unread[Set aside]
Unread -->|one tap, six at a time| Gemma
Gemma -->|JSON facts, cached per SMS| Mark
Mark[markCards → markCardPayments →<br/>markOwnTransfers → your overrides]
Store[(booji.json<br/>your rules, overrides, notes,<br/>fates, aliases, balances, goal)] <--> Mark
Store <--> Ledger
Mark --> Ledger[Ledger<br/>summary · balance with roll-forward ·<br/>recurring · forecast · festivals · drains]
Ledger --> UI[Past · Graveyard · Future · Settings<br/>Jetpack Compose]
Ledger -->|finished numbers as text| Gemma
UI -->|Summon / sort / read| Gemma
Gemma[Gemma 4 E2B on the phone<br/>via LiteRT-LM, GPU then CPU<br/>reads · sorts and tidies names · writes]
Gemma -->|the ghost's words| UI
Gemma -->|categories + clean names| StoreThere are three architectural decisions worth calling out.
First, every input to Gemma is text, never a calculation it needs to perform. It receives an unreadable SMS, a list of merchant names, or a JSON containing numbers Kotlin has already worked out.
Second, everything Gemma returns is cached. A message is read once, a merchant is categorised once, and the user's own corrections take precedence over the model's suggestions.
Finally, there is exactly one network connection in the entire application: the initial model download from Hugging Face. After that, everything happens on the device.
The accountant does the accounting.
Every number in Booji is calculated in Kotlin from the SMS data. Gemma is deliberately kept away from the actual arithmetic.
Here's how the ledger works:
Parsing: Regular expressions extract the amount, transaction direction, counterparty, account tail and quoted available balance. OTPs, future debit reminders, card bill due notices, offers and payment requests are rejected. If both a UPI app and the bank send an SMS about the same payment, the message containing the bank balance takes precedence.
Balance: Each account starts with the latest balance reported by the bank. Every subsequent debit is subtracted and every credit added. This is necessary because banks often send transaction notifications without including the updated balance. Bank account balances are combined, while credit cards are tracked separately using their available limits. They are never added to the bank balance. Card bill payments and transfers between your own accounts are matched and excluded from spending to avoid double-counting.
No bank API, no Account Aggregator, no credentials. The bank has already sent the information in a text message.
Corrections: Tap any transaction to see its original SMS and the reason it received its category. You can change the category for that transaction or for every transaction from that merchant, mark it as a transfer between your own accounts, ignore it, correct its direction, or add a note. Categories can also be renamed, and new ones can be created.
Recurring charges: Booji looks for transactions from the same merchant, with amounts within 15% of the median, and gaps clustering around a week, a month or a year. A pattern must appear at least twice before it is considered recurring.
Forecast: This is where the crystal ball gets its numbers. Booji looks at discretionary spending over the previous eight weeks and groups it into seven-day windows. The median window determines the expected daily spending pace, while the 25th and 75th percentiles establish the forecast range. This prevents one particularly expensive weekend from distorting the entire month's projection.
To that, Booji adds recurring charges still due before month-end and the expected salary if it hasn't arrived yet. The projected amount needed for the month is the spending already recorded plus these remaining expenses. The expected leftover is then calculated from income minus that amount.
- Festivals: A normal week and the week before Diwali are rarely comparable. A forecast that ignores the festival calendar is going to be least accurate precisely when you need it most.
Booji includes a small calendar of Indian festivals, covering national festivals and the region selected in Settings. When a festival or its run-up falls within the remaining month, the forecast adds a named allowance.
If your SMS history contains the same or a comparable festival, Booji estimates the additional spending using your own previous spending patterns. Otherwise, it uses a stated typical multiplier and makes that distinction visible in the allowance card.
There are two things it deliberately refuses to do: guess what people in your location might spend (which would require external data), or make assumptions based on your gender. Neither belongs in a personal financial forecast.
There are unit tests covering the parser (HDFC, SBI, ICICI, Axis and PhonePe formats, along with rejected messages), ledger calculations against the demo inbox, and festival allowances.
Building the haunted house.
I wanted the interface to feel like something out of a Christmas ghost story rather than another spreadsheet pretending to be an app.
The headings use Henny Penny and the body text uses Mystery Quest, both Google Fonts released under the SIL Open Font License, so they're safe to ship with the project.
Booji itself is a little witch. The accountant apparently took up hats in the afterlife. It floats alongside the ledger in the Past tab, appears under a full moon in the Graveyard, and stands with its back to you at the foot of the crystal ball, peering into the future.
There's also a spider that lowers itself on its thread when you start scrolling. The tombstones are real stone, and the jars are filled with honey.
On my own phone, the first run parsed 530 bank SMS messages across four months and reconstructed the account balance from the latest available notification.
The screenshots above use the demo inbox, though. My actual bank statements are nobody's business, which is rather the point.
Teaching a 2B model to copy, not count.
One of the more interesting challenges was getting a relatively small model to behave reliably when writing financial summaries.
The first versions of Booji's notes were almost right. Almost was the problem.
A month-over-month difference would be calculated incorrectly by a few thousand rupees. An undecided recurring charge would be confidently labelled a zombie. A negative number would be read aloud as if it were good news.
The solution was surprisingly consistent: stop asking the model to calculate anything.
Every time Gemma made a numerical mistake, I moved that calculation into Kotlin and gave it the finished result instead. Amounts arrive pre-formatted as ₹40,296. Differences arrive as ₹7,213 less than September. The Graveyard data arrives as three separate lists, and the leftover amount is already calculated and formatted.
After that, the ghosts stopped getting their sums wrong. Mostly because they stopped doing sums altogether.
This is the same principle I used last week for Slothie, and it's one I would apply to any assistant working with personal data:
The model is a reader, a sorter and a writer. Never the accountant.
If a figure isn't in the JSON, it doesn't appear in the note. The system prompt explicitly enforces this, and because the weights, prompts and code are open source, anyone can inspect that decision rather than simply taking my word for it.
Why Does Open Innovation Matter?
A bank statement is probably one of the most personal documents on your phone. It tells a story about where you live, who you pay, what you eat, what you spend your weekends doing, and sometimes how much money you have left.
I wasn't comfortable uploading that information to a third-party service, including a model provider. I also didn't particularly want a budgeting application charging me a subscription to point out all the subscriptions I was already paying for.
Open weights made it possible to solve both problems.
Gemma runs directly on the device, which means Booji can work in airplane mode. The INTERNET permission exists for exactly one purpose: downloading the model weights. There's no API key to leak, no per-token bill, no rate limit and no dependency on a third-party service staying available.
The Apache 2.0 weights, open-source runtime and prompts in the repository also mean that the model's responsibilities are inspectable. The promise that Gemma doesn't invent financial figures isn't just a marketing claim. You can look at the code and see how the information is prepared before it reaches the model.
A closed API might have written a wittier ghost. But it would have required my bank messages to leave the phone, and that was a trade-off I wasn't willing to make.
There's also a practical advantage to running an open-weight model locally. Reading a hundred unparseable bank messages and sorting a hundred merchants means a handful of inference calls, cached per message and per merchant. They cost nothing to run again when I correct something.
With a paid API, that would mean additional costs, rate limits and sending the most sensitive data in the application to an external service.
For Booji, open innovation wasn't simply a way to use an open-source model in a hackathon. It was what made the application possible in the first place.
My Agent Session
Booji was built in one night using Claude Code.
I started with the SMS parser and its tests, moved on to the ledger calculations, then built the three screens. The final stretch was spent teaching a 2B model to copy numbers instead of trying to calculate them.
It was a fairly concentrated session of building, testing, finding things that didn't work, and making the ghost a little more useful.
The Spirit of It
There's something rather Dickensian about a chartered accountant who dies with perfectly balanced books and ends up haunting someone whose books are anything but.
Booji gets three visits: the spending that has already happened, the charges that keep returning in the present, and the future that is slowly taking shape through our everyday decisions.
The first time I opened the Graveyard with my demo inbox, I found two tombstones I'd planted deliberately: an abandoned gym membership and a design suite subscription. Together, they were costing more per month than my groceries.
That was the moment the idea clicked for me.
The Ghost of Christmas Past in Dickens's story forces Scrooge to confront the choices that brought him here. The Ghost of Christmas Present shows him the world continuing around him, including the consequences of his indifference. And the Ghost of Christmas Yet to Come shows him a future that is rather less appealing than he imagined.
Booji follows the same progression, only with bank SMS instead of memories, subscriptions instead of gravestones, and a crystal ball powered by Kotlin.
But there's one important thing about the future in A Christmas Carol: it isn't inevitable. Scrooge gets to change it.
So do we.
Bury the zombies, understand where the money is going, and make a few different choices. The future moves.
And Booji, for all its afterlife accounting experience, is quite happy to help.
Prize Categories
- Best Use of Gemma
來源:DEV Community · dev.to











