Workflow tối giản mình build để tự sinh video ngắn 9:16 từ vài dòng
input gửi qua Telegram. Mục tiêu bài này là chia sẻ pattern +
stack để ae nào quan tâm tự build phiên bản của mình — không
phải tutorial copy-paste.
Bối cảnh
Mình là marketer, gần đây học AI và muốn tự làm content video ngắn
cho dự án cá nhân. Ví dụ trong bài là một kênh nhỏ giả định
“DailyTrivia 60s” — fun facts kiểu “sự thật bạn
chưa biết”, 60-90s/video. Ae thay bằng niche nào cũng được, flow y
chang.
Vấn đề kinh điển: làm thủ công 1 video ngốn 2-3h editing, thuê
freelancer thì đắt + không đều, bỏ vài ngày là tụt reach. Mình ghét nhất
phần edit, còn phần ý tưởng thì thích. Nên mình ráp
pipeline để giữ creative cho mình, đẩy labor cho bot — gửi vài bullet
qua Telegram → vài phút sau có MP4 9:16 đăng lên trang.
2 demo bot đã chạy thật
Trước khi đào sâu pipeline, ae xem trước 2 video bot render từ URL
thật — input chỉ là 1 link paste vào Telegram, không cần tự viết
script.
Demo 1:
Paste link GitHub repo → video giới thiệu dự án
Input:
https://github.com/microsoft/markitdown
Bot fetch GitHub API (stars, forks, language, topics, description),
đẩy cho Gemini sinh kịch bản 6 scenes, TTS giọng nam
(vi-VN-NamMinhNeural), render listicle ~57 giây:
→ Hay dùng khi thấy 1 repo open-source xịn, muốn share lên FB/TikTok
mà không tự viết caption.
Demo 2: Paste
link VnExpress → video tóm tắt tin
Input:
https://vnexpress.net/ca-chua-thanh-bieu-tuong-ap-luc-gia-thuc-pham-o-my-5080124.html
Bot fetch HTML bài báo, strip noise (ads, sidebar, related), đẩy main
content cho Gemini tóm tắt 4 key points → video tin ~27 giây, giọng nữ
(vi-VN-HoaiMyNeural):
→ Hay dùng khi re-distribute tin báo chí thành format video ngắn (nhớ
credit nguồn ở caption).
Lưu ý: 2 video trên mình compress về ~720p để upload blog. Bản
gốc bot render 1080×1920, sắc nét hơn nhiều.
Pipeline tổng thể
┌───────────────┐ ┌────────────────┐ ┌────────────────┐ ┌──────────────┐
│ INPUT │ → │ AI ENGINE │ → │ TTS RENDER │ → │ VIDEO │
│ qua Telegram │ │ LLM + Vision │ │ Edge TTS │ │ HTML → MP4 │
│ (3 dạng) │ │ + Fetch URL │ │ → audio.mp3 │ │ 9:16 1080p │
└───────────────┘ └────────────────┘ └────────────────┘ └──────┬───────┘
│
▼
┌──────────────────────────────┐
│ UPLOAD social (Graph API) │
└──────────────────────────────┘
(Nếu sơ đồ vỡ trên mobile: input Telegram → AI sinh JSON → TTS
audio → HTML capture frame ghép MP4 → upload social.)
Mỗi video tốn ~2-3 phút compute. Toàn bộ chạy free tier — không tốn
$/video.
Stack từng tầng
| Tầng | Tool | Vai trò |
|---|---|---|
| Bot interface | python-telegram-bot |
Nhận input, inline keyboard confirm/edit/cancel |
| LLM | Gemini 2.5 Flash (free tier) | Sinh script + voice text, JSON schema enforce |
| Vision (ảnh) | Gemini Vision | OCR + describe ảnh → feed vào LLM cùng caption |
| Fetch URL | requests + readability |
Lấy main content bài báo, strip noise |
| TTS | Microsoft Edge TTS (free) | Neural voice nam/nữ tùy mood |
| Render video | HTML/CSS/GSAP + headless Chrome + ffmpeg | Capture frame → encode MP4 1080×1920 |
| Upload | FB Graph API (hoặc TikTok / YouTube Data API) | Đẩy MP4 + caption lên social |
Stack này có chủ ý: mỗi tầng đều có free tier ổn, ae
build thử không tốn xu nào.
3 loại input bot nhận
Đây là phần ae hay hỏi nhất. Cùng 1 bot, 3 cách gửi input khác nhau —
mình minh hoạ luôn cả dialog Telegram cho ae dễ hình dung.
(a) Text thuần — gõ vài bullet
point
Đang đi đường, mình nảy ra ý tưởng. Mở Telegram, gõ:
/new
Chủ đề: 5 sự thật về kim tự tháp Giza
- xây khoảng 2560 TCN
- cao ban đầu 146m
- gồm hơn 2 triệu khối đá
- không dùng nô lệ, mà là thợ lành nghề
- vẫn còn 80% nguyên bản
Bot: > Nhận chủ đề “kim tự tháp Giza” — 5 facts. Đang gen kịch
bản… ⏳ > ✅ Kịch bản xong. Voice: nữ trầm. Đang render (~90s)… >
🎬 Video xong (62s). [MP4 preview]. Đăng? [✅ Đăng] [✏️ Sửa] [🗑️ Bỏ]
Bấm ✅. Bot upload, reply link bài đã đăng. Tổng thao tác:
~30 giây gõ.
Đằng sau, bot đẩy text vào Gemini với prompt yêu cầu trả về JSON
kiểu:
{
"title": "5 sự thật bất ngờ về Kim tự tháp Giza",
"items": [
{"headline": "2560 TCN", "detail": "Xây cách đây gần 4.600 năm"},
{"headline": "146 mét", "detail": "Cao nhất thế giới suốt 3.800 năm"}
],
"voice_script": "Bạn có biết kim tự tháp Giza đã đứng vững gần 4.600 năm..."
}
→ Render thẳng thành video listicle. Use case này nhanh nhất, mình
hay dùng khi đã có ý tưởng sẵn trong đầu.
(b) Image +
caption — upload ảnh kèm chú thích ngắn
Đang lướt mạng, thấy 1 bản đồ cổ hay phết. Screenshot, gửi vào bot
kèm caption:
[Ảnh: bản đồ cổ thế kỷ 16]
caption: "Bản đồ này vẽ California là 1 hòn đảo — sai lầm địa lý kéo dài 100 năm"
Bot: > Nhận ảnh — đang OCR + phân tích visual… 👁️
~20s sau: > ✅ Đọc nội dung ảnh xong. Sẽ dùng ảnh làm hero visual
mở đầu, animate fact theo timeline. Confirm? [✅ Go] [✏️ Edit]
Bot làm 2 việc song song: 1. Gửi ảnh + caption cho Gemini Vision →
đọc ảnh, đối chiếu caption, sinh script 2. Giữ luôn ảnh làm hero visual
slide 1 (cropped khung 9:16, có safe area cho text)
→ Video có visual gốc làm điểm tựa, không bị cảm giác “AI thuần”. Ae
lười nghĩ bullet, cứ thấy ảnh hay là quăng cho bot.
Trick chính: Vision không chỉ slap ảnh lên video —
nó đọc ảnh ra text có cấu trúc, AI dùng cả text + ảnh gốc để dựng kịch
bản.
(c) URL bài báo — paste link
là xong
Sáng đọc báo, thấy 1 bài hay. Paste thẳng link:
/new https://example.com/article-about-octopus-intelligence
Bot: > Đang fetch + summarize bài… 📰
~40s sau: > ✅ Summary xong: bài 800 chữ → 6 key facts. Format đề
xuất: listicle 60s. Xem outline? [✅ Đăng] [📋 Outline]
Bot fetch HTML, strip boilerplate (header/footer/ads), lấy
<article> chính, đẩy cho Gemini với prompt “tóm
tắt 5-7 bullet thú vị nhất, viết lại tiếng Việt tự nhiên, kèm voice
script”.
→ Hữu ích khi ae đọc được bài hay nhưng lười viết lại. Lưu ý: mình
credit nguồn ở caption để không bị issue bản quyền.
Behind-the-scenes — mỗi tầng
làm gì
LLM sinh script
Mình không để LLM viết free-form. Bài học xương máu: nó
sẽ lan man, sai số liệu, lệch format. Cách fix: định
nghĩa JSON schema cứng (title, items[],
voice_script, hashtags[]), bật
response_mime_type: "application/json" +
response_schema, system prompt kèm 2-3 few-shot example
đúng tone. → Output đồng đều, parse trực tiếp không cần regex tay.
TTS render audio
Edge TTS gọi đơn giản: voice name + text → mp3. Mình lưu ý:
- Chèn
<break time="300ms"/>SSML giữa các ý → nhịp
đọc tự nhiên hơn - Voice nữ giọng miền Nam mình thấy “trivia friendly” nhất, nhưng tùy
niche mà chọn - Lưu cache theo hash text → đỡ render lại khi edit script vặt
Render HTML → MP4
TL;DR cho ae non-tech: viết template như viết web → headless
Chrome chụp từng frame → ffmpeg ghép thành MP4. Mượt, control 100%,
custom dễ.
Mình thử qua tool no-code (template gò + watermark), MoviePy
(animation thô) rồi mới chốt HTML render. Pipeline:
- Template HTML/CSS có sẵn (placeholder)
- Inject data từ JSON LLM trả về
- GSAP timeline điều khiển animation entrance
- Headless Chrome mở trang,
page.screenshot()ở
30fps - ffmpeg ghép frame + audio → MP4 1080×1920, H.264
Ae web dev quen CSS thì custom animation cực dễ. Nhược: tốn CPU lúc
capture (VPS 2 core, ~2 phút/video 60s).
Upload social
FB Graph API có endpoint upload video chunked (/videos).
Mình upload published=false trước → lấy
video_id → set thumbnail từ frame 15 (skip intro animation)
→ published=true kèm caption + hashtag. Swap dễ sang TikTok
Content Posting API hoặc YouTube Data API v3.
Mẫu prompt để ae copy →
paste vào Claude
Phần này cho ae nào muốn build phiên bản của mình. Mở Claude Code
(hoặc Claude.ai) trong 1 folder trống, paste block dưới, để Claude
scaffold project. Có scaffold sẵn thì giảm 80% friction setup — phần còn
lại 1-2 tuần ae iterate là OK.
💡 Chuẩn bị trước khi paste — 3 thứ sau, có sẵn thì
code Claude generate ra chạy được luôn: – Telegram bot
token — tạo qua @BotFather, copy
chuỗi123456:ABC...– Gemini API key —
free tier ở aistudio.google.com/apikey
– FB Page Token — Graph Explorer → exchange long-lived
user token → page token never-expire (docs
FB)
I want to build a Telegram bot that turns user input into short 9:16 vertical videos with AI voice-over, then auto-posts to Facebook.
PIPELINE
Telegram bot → Gemini (script + voice text, JSON schema enforce) → Edge TTS → HTML/CSS/GSAP capture in headless Chromium → ffmpeg encode MP4 → FB Graph API upload
STACK (all free tier)
- python-telegram-bot v21 # bot interface
- google-genai SDK + gemini-2.5-flash # LLM, MUST use response_mime_type="application/json" + response_schema
- edge-tts # neural TTS, output mp3
- playwright (chromium headless) # render HTML to PNG frames at 30 fps
- ffmpeg (system binary) # encode frames + audio → MP4 1080x1920 H.264 AAC
- requests + FB Graph API v22 # upload video to FB Page
SCAFFOLD FILES
/.env.example # TELEGRAM_BOT_TOKEN, GEMINI_API_KEY, FB_PAGE_ID, FB_PAGE_ACCESS_TOKEN
/requirements.txt
/bot/main.py # Telegram handlers: /new <text>, /new <url>, photo+caption
/bot/generator.py # Gemini wrapper, JSON schema enforce, retry on parse fail
/bot/models.py # Pydantic: VideoScript { title, items[], voice_script, hashtags[] }
/bot/tts.py # Edge TTS wrapper, cache by sha256(text+voice)
/bot/render.py # Playwright capture frames + ffmpeg encode
/bot/upload.py # FB Graph upload, pin first comment (answer/source)
/bot/composer.py # Orchestrate: input → script → tts → render → upload
/bot/sources.py # Fetch URL (requests + readability), GitHub API stats helper
/templates/listicle.html.j2 # 1 template enough for v1: hero + 5-8 item cards + CTA outro
/.gitignore # exclude .env, __pycache__, jobs/, *.mp4
CONSTRAINTS
- All video output 1080x1920 (9:16), H.264 video, AAC audio, max 90 seconds
- LLM output MUST conform to JSON schema (no free-form)
- Cache TTS audio by sha256(text + voice_name) to avoid re-rendering on script edits
- Before posting, bot shows MP4 preview + 3 inline buttons: [✅ Đăng] [✏️ Sửa] [🗑️ Bỏ]
- On error, bot replies with stack trace truncated to last 5 lines, then resets conversation state
- Log every render job to jobs/<timestamp>_<slug>/ with manifest.json + audio.mp3 + frames/ + final.mp4
FIRST DELIVERABLE
Build ONLY the text-input flow first (user types /new + bullet points). Skip image-input and URL-input for v1 — add those after text flow works end-to-end. After scaffolding, walk me through running it locally with a dummy bot token + 1 sample run.
Sau khi Claude scaffold xong, ae chạy theo các bước:
pip install -r requirements.txt && playwright install chromium- Copy
.env.example→.env, điền 3 token
(Telegram, Gemini, FB) python -m bot.main→ mở Telegram, chat với bot, gõ
/new+ topic- Khi text flow chạy OK → bảo Claude tiếp “thêm image input
handler (Gemini Vision) + URL fetcher (readability)” - Tới đoạn render đẹp lên → iterate template HTML theo brand của
ae
Mình không share full source code production vì chứa token + prompt
mình đã iterate cho niche cụ thể (nói toẹt — chia sẻ ra cũng không giúp
ae nhiều vì context khác nhau). Nhưng scaffold trên đủ cho ae có MVP
chạy được trong 1-2 tối.
Bài học vận hành
Cái work tốt
- Schema-first cho LLM: ép JSON + few-shot là khác
biệt giữa “demo” và “production”. Đây là cái thay đổi pipeline nhiều
nhất. - Confirm step trước khi đăng: bot luôn show preview
MP4 + nút Đăng/Sửa/Bỏ. Tiết kiệm vô số lần “chết rồi đăng
nhầm”. - Template hoá HTML: 1 template tốt > 10 template
loè loẹt. Đầu tư typography + spacing. - Bot Telegram làm interface: ae thao tác từ điện
thoại được, không cần SSH vào server.
Cái suýt mất thời gian (hơi technical, ae không
code skim qua)
- Font + headless Chrome: phải cài font system trên
server, CDN không load kịp lúc capture. - dotenv parsing: ký tự
#trong CSS
color bị parser coi là comment → quote value lại. - FB Page token: muốn never-expire phải exchange qua
long-lived user token trước. Lấy thẳng từ Graph
Explorer = 1h là die. - Rate limit free tier: Gemini Flash 1.500 req/ngày —
đủ cho cá nhân nhưng phải cache khi retry. - Image hero shot: ban đầu ảnh chiếm full frame đè
title — phải resize + safe area cho text.
Cái mình vẫn đang nghĩ tiếp
- A/B test format (voice nam vs nữ, tempo nhanh vs chậm) chưa có
infra - Cross-post nhiều platform 1 lần — hiện đăng từng nơi một
- Tự sinh thumbnail riêng (không lấy frame video) để CTR cao hơn
Disclaimer — đọc trước khi
build
1. Đây là
phiên bản free tier, không phải bản tốt nhất
Mình chọn stack free vì cá nhân + đang học. Ae muốn ngon hơn, hoàn
toàn có thể swap:
- LLM trả phí: Claude (Sonnet/Opus), GPT-4, hoặc
Gemini Pro với cap cao hơn → chất lượng script + reasoning tốt hơn rõ
rệt, đặc biệt khi cần fact-check kỹ - TTS premium: ElevenLabs (giọng tự nhiên gần như
người thật, có voice cloning), Azure Neural premium, PlayHT → nâng cảm
xúc và intonation lên một bậc - Render video: nếu lười dựng HTML template →
Remotion (React-based, docs xịn), hoặc tool no-code như Pictory /
InVideo (nhưng mất control) - Auto-post chuyên nghiệp: Buffer / Hootsuite / Later
cho UI thân thiện; Make.com / Zapier nếu ae muốn nối nhiều platform mà
ngại code; Publer cho social bulk
Trade-off rõ ràng: trả tiền = chất lượng + ổn định + đỡ tự code. Free
= học nhiều + customize sâu hơn. Tuỳ ae ở đâu trên đường cong đó.
2. Mình không share full code
Nói thẳng: repo của mình chứa API token, schedule riêng, prompt đã
iterate cho niche cụ thể, một số file config cá nhân. Public ra rủi ro
lộ key + cũng không giúp ae nhiều vì context khác nhau.
Bài này mình chia sẻ pattern + stack + bài học — đủ
để ae nào quen code Python tầm 1-2 tuần là dựng được phiên bản của mình.
Ae nào kẹt chỗ nào cứ ping mình trong group, mình giúp được gì sẽ giúp
🤝
Build cho vui + để học AI. Không phải agency, không bán dịch vụ.
Có gì sai/thiếu cứ comment, mình update.