Documentation menu
Getting started
New in v2.0
Feature deep dives
Reference
Tutorials
Telegram Forwarding Tutorials & Recipes
Reproducible recipes: first task, many-to-many routing, anti-spam cleaning, branding with shortcodes, AI rewriting, protected channels and burst-safe delivery.
Verified against the checkout built for v2.2.4 · updated 20 Oct 2026
On this page
Eight recipes, each one a task you can finish in a few minutes. They are written against the current release: the commands are real, the endpoints exist, and the log signals are the ones the stack actually emits.
Your first task, end to end
BeginnerForward everything from a news channel into your own broadcast channel, as your own post rather than a forward.
- Add the bot as an administrator of the destination with permission to post messages.
- In Telegram, open the bot and send
/settings, then choose Create task. - Enter the source:
@MarketNews(or the numeric id for a private channel). - Enter the target:
@MyBroadcast. - Pick Copy mode so the post arrives without a "Forwarded from" banner.
- Save and enable. The task appears in
atf rules listimmediately.
atf rules list # confirm the task exists
atf rules simulate <id> --text "Test post" # dry run, nothing is sent
atf logs tail --follow -n 20 # watch the first real deliveryMany sources into many targets
RoutingOne task can watch several channels and publish to several destinations. Use it when a topic set should be mirrored as one stream.
- Create a task with the first source and the first target.
- Add the remaining sources:
atf rules sources <id> add -1001111111111. - Add the remaining targets the same way, or edit the task in the dashboard with the target list editor.
- Address a forum topic on a target with the topic id so the mirror lands in the right thread.
- Decide the policy once — every source in the task is read with the same rules.
atf rules create --name "Market news" --source -1001111111111 --target -1003333333333
atf rules sources <id> add -1002222222222
atf rules show <id> --json | jq '.source_chats, .target_chats'Scrub ads, links and mentions
CleaningTurn a channel full of sponsor links into clean text without breaking the formatting of what remains.
- Open the task settings and enable Remove URLs, Remove mentions and Remove hashtags.
- Add sponsor phrases to the custom word list — folding means one entry catches Latin and Arabic-script spellings.
- Enable phone-number stripping if the source posts contact numbers.
- Add a blacklist rule if any post containing a word should be dropped entirely rather than cleaned.
- Preview with the playground or
atf rules simulatebefore enabling on live traffic.
atf rules settings set <id> clean_links=true
atf rules settings set <id> clean_mentions=true
atf rules settings set <id> clean_hashtags=true
atf rules settings set <id> custom_words="Sponsor,Join VIP,50% Off"
atf rules simulate <id> --text "Join VIP t.me/spam @admin #deal"Brand with header and footer shortcodes
BrandingCredit the original channel with a real clickable link, and stamp every delivery with its origin.
- Set a header template and a footer template on the task.
- Use shortcodes for anything that changes per message — origin name, post id, date and link.
- Check the rendered result in the playground before saving.
- Note that only validated shortcodes interpolate; an unknown token is left as written rather than silently dropped.
Header: 📢 [[ORIGIN_NAME]] · post [[ORIGIN_POST_ID]]
Footer: ─────────────
🔗 Original: [[ORIGIN_NAME_URL]]
📅 [[ORIGIN_POST_DATE]]Rewrite posts with an AI provider
AIRe-headline and re-word a source feed so the mirror reads as your own editorial voice, without letting a provider outage break delivery.
- Create an AI configuration: provider, model and a prompt template (or start from a preset).
- Test it against sample text before wiring it to live traffic.
- Enable rewriting on the task only — it is per task, not global.
- Watch the first deliveries in the log history to confirm the rewrite stage ran and how long it took.
- If the provider is unreachable, the cleaned text is delivered unchanged; the job is not failed.
curl -s -X POST localhost:8080/api/ai-configs \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"provider":"openai","model":"gpt-4o-mini","prompt_template":"Rewrite for a channel: {text}"}'
curl -s -X POST localhost:8080/api/ai-configs/<id>/test \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"text":"Sample post from the source channel"}' | jqMirror a copy-protected channel
AdvancedWhen a channel forbids forwarding, the pipeline downloads the media and re-uploads it instead of failing.
- Nothing to configure: the fallback engages automatically when Telegram refuses the native forward.
- Expect the log signal
[PIPELINE:RESTRICTED_BYPASS]on those deliveries. - Check disk headroom — re-upload paths briefly stage media locally.
- For large archives, import a session and use ordered lanes so a backfill does not reorder the channel.
atf logs query --search "RESTRICTED_BYPASS" | head -20
df -h . # re-upload staging needs headroomDebug a missing delivery with log history
OpsThe fastest way to answer "why did that post not arrive" is to follow one message through the pipeline.
- Query the task's own history first:
GET /api/rules/<id>/logsor the dashboard log explorer filtered by task. - Read the last stage that logged for that message: a FILTER_DROP explains a deliberate skip, a QUEUE without DELIVERED means workers are stuck.
- Check the queue depth and dead-letter list if the job never left the queue.
- Compare the correlation id with the API call that created or changed the task.
atf logs query --level ERROR | head -30
atf data queue
atf data dlq --replay # only after the cause is fixedSurvive a burst without reordering
ReliabilityA channel that posts ten updates in a minute will not arrive out of order, and a rate limit will not lose any of them.
- Leave ordering on for tasks where sequence matters — deliveries are serialized per task.
- Keep tasks separate rather than merging unrelated feeds, so one slow target cannot delay another stream.
- When a FloodWait hits, deliveries pause and jobs stay queued; nothing is dropped.
- Verify after the fact: queue depth returns to zero and the DLQ stays empty.
atf status --watch # watch the board drain
atf data queue --json | jq '.pending, .inflight'
atf data dlq # expect empty