Telegram moderation

Scammers move faster than moderators.

Qalqon watches Telegram groups for fraud — advance-fee scams, fake offers, phishing links and disguised malware — across languages. It removes what is clearly an attack, and asks a human about everything else.

6independent signals
∞languages
475automated tests
<1stypical decision
The problem

The scam and the honest post look identical.

Real communities talk about money constantly — buying and selling, splitting bills, arranging payments, helping each other out. A filter tuned to flag those buries the group's purpose in false alarms, and eventually removes the members who post most usefully. An early version of this bot flagged four of the fifteen real messages now kept as its test corpus, and banned two of them outright.

Left alone
“Anyone still selling the spare monitor? Happy to pay your price — message me”
Every keyword here appears in scams too. Nothing is being asked of anyone, so nothing happens.
Removed
“I've got one — just send the 300 deposit first and I'll ship it today”
Same subject, same tone. But money is demanded before anything is delivered.

The topic is never the signal. What separates the two is whether someone is asked to part with money or credentials before receiving anything. Qalqon is built around that distinction rather than around keywords about money — which is also why it works the same way in a language it has never been tuned for.

How it works

Six signals, one decision.

Cheap detectors run first; the language model only when it can still change the outcome. The signals are combined, never averaged — and the rules that decide what happens are a pure function with no network and no database, so every case can be tested.

Messagetext · file · editSIGNALSKeywordsmultilingualLinkstyposquatsFilesdisguisedPaceflood · raidProfilephoto · bioModelcontext-awarePolicypure rulesAllowReviewDeleteRemove

Context-aware

The model is told what is normal for the community it guards, so an ordinary post quoting a price reads as everyday trade rather than an earnings promise.

Structural link checks

Homographs, typosquats, credentials-in-URL and buried double extensions. Nothing is ever fetched — following a stranger's link would hand them an SSRF probe.

Memory that forgives

Repeat offenders escalate; strikes expire, so one bad week does not follow someone forever. Long-standing members can never be banned automatically.

Safety

Built to be wrong safely.

A moderation bot's worst failure is not missing a scam. It is removing a real member — and doing it invisibly.

Two signals to act

A suspicious message alone is not enough. The sender's profile has to agree before anyone is removed; a lone red flag goes to a human instead.

Dry run by default

New deployments report what they would do without touching anything, so the thresholds can be judged against real traffic before they are trusted.

Failure is loud

If a detector cannot run, the verdict is marked degraded and admins are told. A screening step that quietly stops working is worse than one switched off.

One-tap reversal

Every alert carries Ban, Ignore and Trust buttons. Marking a decision wrong clears the strike behind it, so a mistake cannot compound.

Privacy by retention

Ordinary conversation is never stored. Only messages that were acted on are kept, and only for 90 days.

Measured, not assumed

Admin decisions are recorded as ground truth, so the false-positive rate is a number on a dashboard rather than a feeling.

Built with

Plain Python, tested hard.

Event-driven Model–View–Controller: the controller translates Telegram updates, the model holds every rule and all state, and views subscribe to what it announces. The model imports no Telegram code at all, which is what makes the whole decision path testable without a network.

Python 3.12 python-telegram-bot Groq · gpt-oss-safeguard Hugging Face FastAPI SQLite · WAL Docker pytest · 468 tests nginx · Let's Encrypt

No network in the tests

Telegram, Groq and Hugging Face are all substituted, so the full escalation matrix runs in seconds on every change.

Read-only dashboard

The web process opens the database read-only and mounts it read-only. Its buttons ask the bot to act, so a bug here cannot alter moderation state.

Deploys as one container

No published ports — the bot polls outbound, and its only listener stays on the private container network, so it adds no attack surface to the host.