Personal Exchange Bot
Helps Russians living abroad exchange rubles for local currency, person to person, right in Telegram. Built by a team of two people and a crew of Claude Code agents, in production since October 2026.
A simplified walkthrough: create an offer, get responses, agree on a place, exchange and rate. The bot never holds money.
- Role
- Co-developer, team of two
- First version
- 1 October 2026
- Status
- In production
- Code
- Private, demo on request
Rubles abroad: no banks, only chats.
Since the 2022 sanctions, most banks and exchange offices in Europe no longer exchange rubles. Yet many Russians live abroad and need local money, like Czech koruna. Until now they swapped it in Telegram chats.
-
Posts typed by hand
Structured offers
You post what you give and what you want; others accept or suggest their rate.
-
Rates pushed by changers
Rates to compare
Market rate from ExchangeRate-API, exchanger rates from BestChange, Revolut and Wise for comparison.
-
Scammers in the chat
Checks and reputation
Phone and account checks, spam databases, weighted ratings, a risk score and complaints.
327
pull requests merged, each through CI and review
173
screens in the bot
2
days from the spec to a working first version
3
languages: English, Ukrainian, Russian
How a deal works
Four steps, all inside one Telegram chat.
-
Post an offer
“I give 50,000 RUB, I want 13,500 CZK.” Market and exchanger rates show whether the price is fair.
-
Get responses
Other people accept your rate or suggest their own. Ratings show who you are dealing with.
-
Choose one
You pick a response, and a private deal chat opens between the two of you.
-
Meet and rate
Agree on a place on the map, exchange cash, confirm and rate each other.
The bot never holds money. It runs the deal: relays messages in the deal chat, collects confirmations and ratings.
VIP offers
Offers can be promoted for Telegram Stars.
Inline mode and city channels
Share offers in any chat, or follow a channel for your city.
Moderation
A web admin and an Android app for the moderators.
Under the hood
Python 3.14 and aiogram 3 on our own server, with every piece of state in PostgreSQL.
Telegram
Users, inline mode, city channels, Telegram Stars
Android admin app
Kotlin, Jetpack Compose
Webhook over HTTPS
Hetzner Cloud, Germany. Docker.
Caddy
HTTPS
Bot
aiogram 3, 173 screens
Worker
Background jobs, rate updates
Admin
FastAPI web, two-factor login
Mini App
Meeting map, MapLibre and OpenStreetMap
PostgreSQL
Users, offers, deals, ratings, the notification queue and dialog state. SQLAlchemy and Alembic.
Rate sources
ExchangeRate-API
Market rate
BestChange
Exchanger rate
Revolut and Wise
For comparison
Stateless bot
Even dialog state lives in PostgreSQL, so the bot process itself keeps nothing in memory.
Fresh rates
A background worker refreshes rates from four sources and stores them in the database.
Safe releases
A new release first goes live on trial in production and rolls itself back if the checks fail.
Trust is the product
Exchange chats are full of scammers, and people meet strangers to swap cash. These are the safety features I built so they can trust each other.
-
Encrypted deal log
Every deal chat is logged, and the log is encrypted.
-
Account checks
Phone verification, Telegram account age, and lookups in the CAS and LOLS spam databases.
-
Weighted reputation
A reputation score built from weighted ratings.
-
Risk score
A risk score that alerts moderators.
-
Complaints
Users can file a complaint about a deal.
Quality
4,808
automated tests in pytest. Every pull request has to pass all of them.
- Unit tests for the core logic.
- Integration tests against a real PostgreSQL database.
- End-to-end tests that run the bot, the worker and the admin together.
- Property-based tests with Hypothesis, for inputs nobody thought of.
4 of 4 checks passed
GitHub Actions, on every pull request
- ruffLint
- mypyType checks
- pytest4,808 tests
- gitleaksSecret scan
I set up the test infrastructure underneath: database fixtures, a test bench for the bot and the end-to-end tests.
My role
The idea and the original spec came from my teammate. We write the issues, make the decisions and check the results; Claude Code agents write the code and the tests. 153 of the 365 commits and 103 of the 327 merged pull requests are mine.
What I built
- Version one in two days. From the spec, on 30 September and 1 October: project skeleton, database, bot, offer feed, responses, deals, ratings, notifications, moderation, the web admin with two-factor login, Docker and tests.
- Interface and design. Admin redesign, mobile version, System and Stats sections, ban management, and the Android admin app.
- Trust and safety. Encrypted deal log, account checks, weighted reputation, risk score, complaints.
- Rates and languages. Rate sources, reading amounts like “1000 euro” or “€1000” from text, Ukrainian and the language packs.
How we work with agents
- One issue, one agent. A session manager watches the repo and starts a headless Claude Code agent for each GitHub issue, in its own git worktree and a fresh session.
- Labels set the power. Issue labels pick the model, from
model:haikutomodel:opus, and the effort, fromeffort:lowtoeffort:max. Without labels the agent runs on Opus at xhigh effort. - Confirm before coding. The agent posts how it understood the issue, with sample screens where relevant, and only then starts coding.
- Plain-language handoffs. Agents hand off to the next session and talk through issue comments a person can read.
- Tooling I built for them. Environment checks, a map of all 173 screens and automatic handoff between sessions.
Want a walkthrough?
The repository is private, so I’m happy to show the code, the tests and the admin panel on a call.