Documentation menu
Getting started
New in v2.0
Feature deep dives
Reference
Per-task content rules
Per-Task Telegram Content Rules and Filters
Blacklists, whitelists, link handling, regex gates, length limits and ordered replacements — evaluated cheapest-first, with per-source and per-target overrides.
Verified against the checkout built for v2.2.4 · updated 20 Oct 2026
On this page
FilteringNew in v2.0
Word-boundary, Unicode-folded matching that behaves the same in Persian, Arabic and English.
What a policy contains
| Field | Meaning |
|---|---|
| blacklist_words | Never forward a message containing one of these words. |
| whitelist_words | Forward only when these words are present. |
| whitelist_mode | any (default), all, or phrase — how the whitelist is satisfied. |
| block_links | Refuse any message that contains a link. |
| remove_links | Forward the message with links stripped out. |
| strip_words | Forward the message with these words removed. |
| replacements | Ordered find → replace pairs, literal or re: regex. |
| regex_block / regex_require | Refuse on match / require a match; hostile patterns are rejected. |
| min_length / max_length | Length gates applied before any expensive work. |
| allowed_media / blocked_media | Media allow and deny gates. |
| case_sensitive / word_boundary / normalize | Matching semantics, all overridable per task. |
The matching semantics that matter
- Unicode folding: Persian and Arabic spell the same letter several ways (ي vs ی, ك vs ک, أ vs ا) and use zero-width joiners. Both the message and the rule are folded before comparison, so one key catches every spelling.
- Word boundaries: cat does not match concatenate. Keywords that begin or end with a non-word character (#promo, @channel, a URL) skip the boundary check, because a boundary there would be wrong.
- Cheapest-first evaluation: lengths, media, links, literal words, then regexes. A burst of fifty posts never pays for regexes it does not need.
- Single-pass replacements: replacements run over the original text in one pass, so a replacement value can never be re-scanned by a later rule. No cascades, no surprises.
- Patterns are compiled once and cached, and nested-quantifier patterns like (a+)+ are refused outright rather than trusted with a worker.
Overrides are additive only
The task policy is the floor. A per-source or per-target override can add constraints — a stricter blacklist for a public target, link stripping only for the free tier — but every policy in the chain must allow the message for it to be delivered.
Transforms apply in chain order: task first, then source, then target. That ordering is deterministic and visible in the log entry, which records the policy provenance that decided the outcome.
Dry-run a policy before it goes live
# does this message survive my task's policy?
atf rules simulate <task-id> --text "Join our VIP group at t.me/vip — 50% off today"
# settings are validated before they are written
atf rules settings schema
atf rules settings set <task-id> remove_links=true block_links=false
atf rules settings get <task-id>Frequently asked
Will a blacklist I typed in Persian catch text written on an Arabic keyboard?
Yes. Letter variants (Arabic yeh, alef forms, kaf) and zero-width joiners are folded before comparison, so both spellings normalise to the same string.
Can a per-target override loosen the task rules?
No. Overrides are additive by construction: each policy in the chain must allow the message. A target can be stricter than the task, never more permissive.
What happens to a message that a rule rejects after it was already delivered?
Edits are re-evaluated against the policy; if the edited message no longer passes, the destination copy is deleted. Set max_time_edit to bound how far back edits are honoured.
Are regex patterns safe to hand to this system?
Patterns are compiled once and cached, and nested-quantifier constructions are refused at validation time instead of being run against traffic.