- date written
- minutes to read
- 6
- sources cited
- 10
Editing a live website from Telegram, with a human still approving
A Telegram bot edits prices and contact details on the site; I press Approve and the new version ships. Three layers at the entry point, eight permitted commands, one ordering trap.
- check gates
- ai with a human approver
- operations
My fire-door sales site carries unit prices, VAT, a deposit rate and a hotline number, and they change from time to time. If every change means opening a laptop, opening the tooling and running a deploy command, the change gets postponed. So I built a Telegram bot: message the bot, the bot edits, the checks run on their own, I press Approve, the new version goes live.
What is worth writing down is the three decisions behind it, because each one had a tidier-looking alternative that was wrong.
Three decisions
1. The bot goes through the repository instead of editing the running server. The running server is built from the repository. Edit it directly and the next deploy overwrites the change, taking the rollback history with it. Going through the repository makes every change a commit carrying the name of whoever pressed Approve, which is how the price file and the company details file already keep their history. The bot reads the file from the main branch, changes exactly one value, and pushes to a branch of its own, leaving main untouched.
2. The bot goes through the same gate I do. The site's checks live in the repository and run automatically. They are the same checks I pass through when I edit by hand. Giving the bot its own check path would create a second gate, and two gates drift apart sooner or later. The bot's CI config triggers on every branch the bot pushes: run the tests, build the site, three content gates, a CSS check, then send the diff back to Telegram with a GREEN or RED status.
After the merge into main, the checks run again before the release: the first run checks the branch, the second checks the merge result. Two green sides merging into a red result is ordinary.
3. There is an Approve button. This site sells a regulated product: EI fire-resistance ratings, QCVN 06:2022 (the Vietnamese national fire safety code for buildings), prices. One wrong number here is legal risk. Automated checks catch technical faults; "1.9 million typed as 19 million" walks straight through, because both are valid numbers. What catches that is a human eye and one press.
The "Approve and deploy" button appears only when the checks are green; when they are red there is a link to the log and nothing else. The allowed price range is deliberately wide, 500,000 to 20 million dong for a door unit price, because its job is to block digit errors while the right to change prices stays with me. 19 million is still inside the range. What catches it is the warning line on the approval screen:
EI60 painted single leaf unit price: 1,900,000 → 19,000,000 đ/m²
‼️ +900% change (10.00× - check the digit count)
I read that line before I press.
Three layers at the entry point
That message endpoint is write access to a live commercial website, opened by one Telegram secret string. Three layers:
- If the list of permitted users is empty, the endpoint returns 404, meaning it does not exist. Off by default. 404 rather than 403, so anyone probing leaves with nothing.
- The Telegram secret string is compared in constant time. Wrong, rejected.
- The sender ID is checked again at the business layer. A stranger is ignored silently and only logged, because answering tells them what the bot can do. It has to be the numeric ID rather than an @username, because usernames can be changed while the ID stays.
The emergency stop is removing one environment variable; the endpoint returns 404 as soon as the process restarts. Order notifications keep running, because that part is separate.
One detail is mandatory: answer Telegram first, then do the work. Telegram redelivers a message when the reply takes longer than about 60 seconds, and calling an AI model then reading and writing the repository can take longer than that. One beat too slow and a single command creates two branches.
The ordering trap: merge the checks first, turn the bot on second
The order is fixed by how the CI pipeline loads its config: it runs the config file sitting in the branch that was just pushed. The bot's branch is created from main. If main has no check config file, the bot's branch has none either, the gate never fires, and the Approve button never appears. The bot still creates the branch; it just goes quiet afterwards.
The /deploy command can likewise only trigger a config already present on the default branch. Only after the merge does the deploy config exist to be triggered.
So the right order is: merge into main first, then run the command that turns the bot on.
What the bot can change, and where it stops
The bot edits only fields on an allowlist. Eight data-editing commands:
| Command | What it changes | Allowed range |
|---|---|---|
/gia | door unit price by EI rating, finish, leaf count | 500,000 to 20 million |
/phukien | hardware package | 100,000 to 10 million |
/hoso | documentation fee | 0 to 5 million |
/vat | tax rate | 0 to 0.2 |
/coc | deposit rate | 0.1 to 1 |
/hotline | hotline | Vietnamese phone format |
/email | contact email | email format |
/flag | feature flag | fixed list of flags |
Plus /sua, which has the AI draft description copy, and /xem, /deploy and /giupdo, which are read only. Enter a number outside the range and the bot says so plainly: this range blocks missing or extra digits, the right to change prices is still yours, and if the real price falls outside the range, edit the file by hand with the evidence attached.
Four things the bot leaves alone:
- Everything outside the two data and config folders: source code, interface, CSS. A narrow scope is what keeps a leaked secret string confined to a handful of data fields.
- Unknown keys. Creating a new key is the quietest way to corrupt a file while the checks stay green.
- The full sales launch flag and the payments flag. Those two go back through a gate of their own, where the launch conditions are written down exactly once: licences, evidence, the compliance file, image usage rights. The bot checks against the list, reports what is still missing, and leaves the switch to a human hand. This is the legal gate.
- The name or location of the partner factory, even when the AI writes it itself inside
/sua. Blocked at two layers: before the branch is created, and at the content gate before the release.
What I took from it
- "AI proposes, a human approves" runs on three concrete things: a separate branch, an automated gate, and a button. Drop any one of the three and the rule exists only on paper.
- Automated checks catch technical faults. Business faults, the valid but wrong number, are caught only by a human eye. Send the AI through your own gate, on the same check path.
- A narrow scope is the cheapest protection there is. If the secret string leaks, the damage stops at a few allowlisted fields, and every change still passes through my press of the button.
- A wrong activation order can silence the whole system with the logs still clean. Write the order down.
If your company wants a data-editing channel with an approval gate like this one, tell me the context.