Mục tiêu bài học

Sau khi hoàn thành bài học này, học viên sẽ có khả năng:
  • Hiểu được cơ chế hoạt động của Telegram Bot API.
  • Tạo và cấu hình một Telegram Bot hoàn chỉnh.
  • Lấy được chat_id của cá nhân, nhóm, hoặc kênh.
  • Xây dựng API Route trong Next.js để gửi thông báo an toàn.
  • Tích hợp gửi thông báo từ form liên hệ, đơn hàng, hoặc sự kiện hệ thống.
  • Xử lý lỗi, định dạng tin nhắn (Markdown/HTML), và gửi kèm hình ảnh, file.

Video tại:

https://youtu.be/ete4eUzs6-U
https://youtu.be/k1Q5x6It1Zg

1. Tổng quan về Telegram Bot

1.1. Tại sao chọn Telegram để gửi thông báo?

Telegram là một trong những kênh thông báo phổ biến nhất cho ứng dụng web vì những lý do sau:
  • Miễn phí hoàn toàn: Không giới hạn số tin nhắn gửi đi (trong giới hạn rate limit hợp lý).
  • Tốc độ nhanh: Tin nhắn đến gần như ngay lập tức trên cả mobile và desktop.
  • API đơn giản: Chỉ cần một HTTP request là có thể gửi tin nhắn.
  • Đa nền tảng: Người nhận có thể xem trên điện thoại, máy tính, web.
  • Hỗ trợ định dạng phong phú: Markdown, HTML, hình ảnh, file, video, location.

1.2. Các trường hợp sử dụng phổ biến

Trong thực tế, Telegram Bot thường được dùng để:
  • Thông báo khi có khách hàng mới gửi form liên hệ trên website.
  • Cảnh báo khi có đơn hàng mới trên hệ thống e-commerce.
  • Thông báo lỗi (error monitoring) cho dev team.
  • Gửi báo cáo doanh thu hằng ngày, hằng tuần.
  • Cảnh báo bảo mật (đăng nhập bất thường, password reset).
  • Thông báo deployment thành công/thất bại.

2. Tạo Telegram Bot

2.1. Sử dụng BotFather để tạo Bot

BotFather là bot chính thức của Telegram dùng để quản lý các bot khác. Các bước thực hiện:
  1. Mở Telegram, tìm kiếm @BotFather (có dấu tick xanh).
  2. Bắt đầu cuộc trò chuyện và gõ lệnh /newbot.
  3. Nhập tên hiển thị của bot (ví dụ: Softech Notification Bot).
  4. Nhập username cho bot, bắt buộc kết thúc bằng bot (ví dụ: softech_notify_bot).
  5. BotFather sẽ trả về một Token có dạng:
7891234567:AAEhBP9k_xxxxxxxxxxxxxxxxxxxxxxxxxxx
⚠️ Cảnh báo bảo mật: Token này giống như mật khẩu. Bất kỳ ai có token đều có thể điều khiển bot của bạn. Tuyệt đối không commit token vào Git, không paste lên forum, không hardcode vào code frontend.

2.2. Các lệnh hữu ích của BotFather

Một số lệnh thường dùng để tùy chỉnh bot:
  • /mybots — Liệt kê tất cả bot bạn đã tạo.
  • /setname — Đổi tên hiển thị.
  • /setdescription — Đặt mô tả cho bot.
  • /setuserpic — Đổi avatar cho bot.
  • /token — Lấy lại token (nếu quên).
  • /revoke — Thu hồi token cũ và tạo token mới (dùng khi token bị lộ).

3. Lấy Chat ID

Để gửi tin nhắn, bot cần biết gửi đến đâu. Đó chính là chat_id. Có ba loại chat_id phổ biến:
  • Chat cá nhân: ID là số dương (ví dụ: 123456789).
  • Nhóm (group): ID là số âm (ví dụ: -987654321).
  • Kênh (channel): ID là số âm với tiền tố -100 (ví dụ: -1001234567890).

3.1. Lấy chat_id cá nhân

Cách 1: Dùng bot @userinfobot
  1. Tìm @userinfobot trên Telegram.
  2. Bấm Start, bot sẽ trả về thông tin của bạn bao gồm Id.
Cách 2: Dùng API getUpdates
  1. Mở Telegram, gửi tin nhắn bất kỳ cho bot vừa tạo (ví dụ gõ /start).
  2. Mở trình duyệt, truy cập URL sau (thay <TOKEN> bằng token thật):
https://api.telegram.org/bot<TOKEN>/getUpdates
  1. Tìm trường "chat":{"id": ...} trong JSON trả về. Đó là chat_id.

3.2. Lấy chat_id của nhóm

  1. Thêm bot vào nhóm Telegram.
  2. Gửi một tin nhắn bất kỳ vào nhóm (có nhắc đến bot, ví dụ /start@your_bot).
  3. Truy cập https://api.telegram.org/bot<TOKEN>/getUpdates.
  4. Tìm chat_id âm trong response.
💡 Mẹo: Nếu nhóm là supergroup, ID sẽ bắt đầu bằng -100.

3.3. Lấy chat_id của kênh

  1. Tạo một kênh Telegram (channel).
  2. Thêm bot làm admin của kênh (nếu chỉ là member, bot không gửi được tin).
  3. Đăng một tin nhắn trong kênh.
  4. Truy cập getUpdates để lấy ID, hoặc dùng bot @username_to_id_bot.

4. Telegram Bot API cơ bản

4.1. Cấu trúc URL

Tất cả các API của Telegram Bot có dạng:
https://api.telegram.org/bot<TOKEN>/<METHOD_NAME>

4.2. Phương thức sendMessage

Đây là phương thức quan trọng nhất, dùng để gửi tin nhắn văn bản. Các tham số chính:
  • chat_id (bắt buộc): ID của người/nhóm/kênh nhận tin.
  • text (bắt buộc): Nội dung tin nhắn (tối đa 4096 ký tự).
  • parse_mode (tùy chọn): Định dạng tin nhắn — Markdown, MarkdownV2, hoặc HTML.
  • disable_web_page_preview (tùy chọn): Tắt preview link.
  • disable_notification (tùy chọn): Gửi tin nhắn im lặng (không có thông báo âm thanh).
Test nhanh bằng cURL:
curl -X POST "https://api.telegram.org/bot<TOKEN>/sendMessage" \
  -H "Content-Type: application/json" \
  -d '{
    "chat_id": "123456789",
    "text": "Xin chào từ Softech Aptech!"
  }'

5. Tích hợp vào Next.js

Phần này hướng dẫn bạn kết nối website với Telegram để tự động gửi thông báo khi có người liên hệ. Mỗi bước đều có hướng dẫn chi tiết — không cần biết lập trình vẫn làm được.

5.1. Tạo dự án Next.js mới

Bước 1 — Mở Terminal (Command Prompt trên Windows) Nhấn phím Ctrl + ~ (phím cách + phím `) trên bàn phím để mở cửa sổ Terminal ngay trong VS Code. Bước 2 — Gõ lệnh tạo dự án Copy đoạn bên dưới, paste vào Terminal, rồi nhấn Enter:
npx create-next-app@latest my-telegram-bot
Bước 3 — Trả lời các câu hỏi trên màn hình Khi Terminal hỏi, gõ đáp án tương ứng:
  • Would you like to use TypeScript? → gõ Yes rồi Enter
  • Would you like to use ESLint? → gõ Yes rồi Enter
  • Would you like to use Tailwind CSS? → gõ Yes rồi Enter
  • Would you like to use src/ directory? → gõ No rồi Enter
  • Would you like to use App Router? → gõ Yes rồi Enter
  • Would you like to customize the default import alias? → gõ No rồi Enter
Chờ khoảng 2-3 phút để máy tải và cài đặt. Khi thấy dòng chữ màu xanh Success là xong. Bước 4 — Mở dự án
cd my-telegram-bot
code .
Lệnh cuối sẽ mở dự án trong VS Code. Từ giờ bạn làm việc trong cửa sổ VS Code.

5.2. Lưu trữ Token và Chat ID an toàn

Ý tưởng đằng sau

Token Telegram giống như chìa khóa để mở cửa nhà bạn. Nếu ai đó có chìa khóa đó, họ có thể gửi tin nhắn từ bot của bạn. Vì vậy, bạn cất chìa khóa trong một nơi đặc biệt gọi là biến môi trường — nơi mà chỉ máy chủ mới đọc được, không ai khác nhìn thấy.

Cách làm từng bước

Bước 1 — Tạo file cấu hình bí mật Trong VS Code, nhìn sang khung bên trái (gọi là Explorer). Bạn sẽ thấy các thư mục như app, public, src. Nhấn chuột phải vào vùng trắng trong Explorer → chọn New File → đặt tên file là .env.local
Lưu ý: Tên file bắt đầu bằng dấu chấm (.). Đây là file ẩn trên máy, nên bạn sẽ không thấy nó trong Finder/Explorer thông thường.
Bước 2 — Viết nội dung vào file Mở file .env.local vừa tạo, paste đoạn sau:
TELEGRAM_BOT_TOKEN=Thay_Token_Cua_Ban_Vao_Day
TELEGRAM_CHAT_ID=Thay_Chat_ID_Cua_Ban_Vao_Day
Thay Thay_Token_Cua_Ban_Vao_Day bằng token mà BotFather gửi cho bạn (dạng 7891234567:AAEh...). Thay Thay_Chat_ID_Cua_Ban_Vao_Day bằng chat ID mà bạn lấy được ở phần 3. Bước 3 — Kiểm tra file .gitignore Mở file .gitignore ở thư mục gốc. Tìm xem có dòng .env*.local chưa. Nếu chưa có, thêm vào một dòng mới:
.env*.local
Việc này đảm bảo file .env.local không bao giờ bị đẩy lên GitHub — tức là không ai nhìn thấy token của bạn.

5.3. Tạo “cỗ máy” gửi tin nhắn tự động

Ý tưởng đằng sau

Bạn sẽ tạo một hàm — tưởng tượng như một “cỗ máy” có nhiệm vụ: nhận tin nhắn → đóng gói đúng định dạng → gửi đến Telegram. Mỗi lần cần gửi thông báo, chỉ cần bảo máy “làm việc” thôi.

Cách làm từng bước

Bước 1 — Tạo thư mục lib Trong Explorer (khung bên trái), nhấn chuột phải vào vùng trắng → New Folder → đặt tên là lib. Bước 2 — Tạo file telegram.ts Nhấn chuột phải vào thư mục lib vừa tạo → New File → đặt tên là telegram.ts. Bước 3 — Nhờ AI viết code Mở Claude Code (trong Terminal: gõ claude), hoặc dùng ChatGPT/Claude web. Copy prompt bên dưới, paste vào, nhấn Enter:
Prompt để tạo file lib/telegram.ts:
Viết file TypeScript `lib/telegram.ts` trong dự án Next.js với các yêu cầu sau:

1. Định nghĩa interface `SendMessageOptions` gồm: `chatId?` (string), `text` (string bắt buộc), `parseMode?` ('Markdown' | 'MarkdownV2' | 'HTML'), `disableNotification?`, `disableWebPagePreview?`.
2. Định nghĩa interface `TelegramResponse` gồm: `ok` (boolean), `result?` (any), `description?` (string), `error_code?` (number).
3. Viết hàm async `sendTelegramMessage(options: SendMessageOptions): Promise<TelegramResponse>` thực hiện:
   - Đọc `process.env.TELEGRAM_BOT_TOKEN` và `process.env.TELEGRAM_CHAT_ID`
   - Nếu thiếu token thì throw Error 'TELEGRAM_BOT_TOKEN chưa được cấu hình'
   - Nếu thiếu chatId thì throw Error 'Chat ID chưa được cung cấp'
   - Gọi fetch POST đến `https://api.telegram.org/bot${token}/sendMessage`
   - Body gửi: `{ chat_id, text, parse_mode: options.parseMode || 'HTML', disable_notification: false, disable_web_page_preview: false }`
   - Headers: Content-Type: application/json
   - fetch options: method POST, cache: 'no-store'
   - Parse response.json(), nếu data.ok === false thì throw Error(data.description)
   - Catch errors, log console.error, re-throw
Sau khi AI viết xong → copy toàn bộ code → paste vào file lib/telegram.ts → lưu file.

5.4. Tạo “cổng” để website gửi thông báo

Ý tưởng đằng sau

Khi khách điền form liên hệ trên website, website cần một cổng để gửi thông tin đó đến Telegram. File này chính là “cổng” đó.

Cách làm từng bước

Bước 1 — Tạo thư mục api bên trong app Trong Explorer, mở thư mục app. Nhấn chuột phải vào appNew Folder → đặt tên là api. Nhấn chuột phải vào api vừa tạo → New Folder → đặt tên là notify. Bước 2 — Tạo file route.ts Nhấn chuột phải vào thư mục notifyNew File → đặt tên là route.ts. Bước 3 — Nhờ AI viết code
Prompt để tạo file app/api/notify/route.ts:
Viết file TypeScript `app/api/notify/route.ts` trong Next.js App Router với các yêu cầu:

1. Import `NextRequest, NextResponse` từ 'next/server'
2. Import `sendTelegramMessage` từ '@/lib/telegram'
3. Export hàm async `POST(request: NextRequest)` thực hiện:
   - Đọc `request.json()` để lấy body chứa `{ name, email, phone, message }`
   - Validate: nếu thiếu `name` hoặc `email` hoặc `message` → trả `NextResponse.json({ error: 'Vui lòng nhập đầy đủ thông tin' }, { status: 400 })`
   - Tạo biến `text` là chuỗi HTML định dạng tin nhắn Telegram với các trường: icon 🔔 tiêu đề "Liên hệ mới từ Website", 👤 Họ tên, 📧 Email, 📱 Số điện thoại (hiển thị "Không có" nếu không có), 💬 Nội dung, ⏰ Thời gian (dùng toLocaleString 'vi-VN' với timeZone 'Asia/Ho_Chi_Minh')
   - Tất cả giá trị động phải escape HTML bằng hàm escapeHtml: thay & → &amp;, < → &lt;, > → &gt;
   - Gọi `await sendTelegramMessage({ text })`
   - Trả `NextResponse.json({ success: true })`
   - Catch errors: log console.error → trả `NextResponse.json({ error: 'Có lỗi xảy ra, vui lòng thử lại' }, { status: 500 })`
   - Viết hàm `escapeHtml` ở cuối file
Sau khi AI viết xong → copy → paste vào app/api/notify/route.ts → lưu.
Phần codeNghĩa tiếng Việt
POSTCổng này chỉ nhận dữ liệu gửi vào (không cho ai đọc ra)
request.json()Đọc thông tin khách gửi lên (tên, email, lời nhắn)
if (!name || !email)Kiểm tra khách đã điền đủ thông tin chưa. Chưa đủ thì báo lỗi
escapeHtml(...)Xóa các ký tự đặc biệt nguy hiểm mà khách có thể gửi vào
sendTelegramMessage({ text })Gọi cỗ máy ở bước 5.3 để gửi tin nhắn đi
NextResponse.jsonTrả kết quả về cho website: thành công hay thất bại

5.5. Tạo trang form liên hệ trên website

Ý tưởng đằng sau

Bây giờ bạn cần tạo giao diện — cái form mà khách hàng nhìn thấy và điền thông tin.

Cách làm từng bước

Bước 1 — Tạo thư mục contact bên trong app Trong Explorer, mở thư mục app. Nhấn chuột phải vào appNew Folder → đặt tên là contact. Bước 2 — Tạo file page.tsx Nhấn chuột phải vào thư mục contactNew File → đặt tên là page.tsx. Bước 3 — Nhờ AI viết code Mở Claude Code (trong Terminal: gõ claude), hoặc dùng ChatGPT/Claude web. Copy prompt bên dưới, paste vào, nhấn Enter:
Prompt để tạo file app/contact/page.tsx:
Viết file TSX page component cho trang liên hệ trong Next.js App Router với:

1. `'use client'` directive ở đầu file
2. Import `useState, FormEvent` từ 'react'
3. State `formData` gồm: `{ name, email, phone, message }` — tất cả khởi tạo là chuỗi rỗng
4. State `loading` (boolean, mặc định false) và `status` ({ type: 'success'|'error'|null, message: string }, mặc định null)
5. Hàm async `handleSubmit(e: FormEvent)`:
   - `e.preventDefault()`
   - setLoading(true)
   - setStatus(null
   - fetch POST '/api/notify' với headers Content-Type: application/json và body JSON.stringify(formData)
   - Parse response.json()
   - Nếu !res.ok throw Error(data.error)
   - Nếu thành công: setStatus success message 'Gửi thành công! Chúng tôi sẽ liên hệ sớm.', reset formData về rỗng
   - Catch: setStatus error message
   - Finally: setLoading(false)
6. UI form có: input name (text), input email (email), input phone (tel), textarea message (rows=5), button submit. Tất cả dùng className Tailwind: w-full px-4 py-2 border rounded-lg
7. Button hiển thị loading ? 'Đang gửi...' : 'Gửi liên hệ', disabled={loading}
8. Hiển thị status.message bằng div có màu: success → bg-green-100 text-green-800, error → bg-red-100 text-red-800
9. Dùng className container: max-w-2xl mx-auto p-6
10. Heading: 'Liên hệ với chúng tôi'
Giải thích đơn giản:
Phần codeNghĩa tiếng Việt
'use client'Đánh dấu đây là giao diện tương tác với người dùng
useStateLưu trữ thông tin form và trạng thái gửi
fetch('/api/notify', ...)Gửi dữ liệu qua “cổng” ở bước 5.4
disabled={loading}Khóa nút gửi khi đang xử lý, tránh gửi 2 lần
bg-green-100Nếu gửi thành công → khung xanh lá
bg-red-100Nếu gửi thất bại → khung đỏ
Bước 4 — Chạy thử Quay lại Terminal, gõ:
npm run dev
Mở trình duyệt, truy cập http://localhost:3000/contact. Điền thông tin và bấm Gửi liên hệ. Kiểm tra Telegram — bạn sẽ thấy tin nhắn hiện lên ngay.

6. Cách nhanh hơn: Gửi thông báo không cần API Route (Server Actions)

Ý tưởng đằng sau

Ở phần 5, bạn tạo một “cổng” riêng (API Route) để gửi dữ liệu. Cách này dùng được, nhưng Server Actions còn đơn giản hơn — không cần tạo cổng, code gọn hơn nhiều.
Khi nào dùng cách nào? Nếu bạn chỉ gửi từ form trên website → dùng Server Actions (phần này). Nếu bạn muốn ứng dụng khác (app mobile, phần mềm bên ngoài) gọi được → dùng API Route (phần 5).

6.1. Tạo Server Action

Bước 1 — Tạo thư mục actions bên trong app Nhấn chuột phải vào thư mục appNew Folder → đặt tên là actions. Bước 2 — Tạo file notify.ts Nhấn chuột phải vào actionsNew File → đặt tên là notify.ts. Bước 3 — Nhờ AI viết code Mở Claude Code (trong Terminal: gõ claude), hoặc dùng ChatGPT/Claude web. Copy prompt bên dưới, paste vào, nhấn Enter:
Prompt để tạo file app/actions/notify.ts:
Viết file TypeScript `app/actions/notify.ts` trong Next.js với:

1. `'use server'` directive
2. Import `sendTelegramMessage` từ '@/lib/telegram'
3. Export async function `sendContactNotification(formData: FormData)`:
   - Lấy name, email, message từ formData.get()
   - Nếu thiếu name hoặc email hoặc message → return { success: false, error: 'Thiếu thông tin' }
   - Tạo text HTML Telegram với icon 🔔 tiêu đề "Liên hệ mới", 👤 name, 📧 email, 💬 message
   - await sendTelegramMessage({ text })
   - return { success: true }
   - Catch: return { success: false, error: 'Gửi thất bại' }
Giải thích đơn giản:
Phần codeNghĩa tiếng Việt
'use server'Đánh dấu đây là hàm chạy ở server, không phải trình duyệt
formData.get('name')Lấy giá trị từ ô nhập “Họ tên” trên form
return { success: false }Trả về kết quả thất bại kèm thông báo lỗi

6.2. Cập nhật trang form liên hệ

Bước 1 — Mở lại file app/contact/page.tsx Thay toàn bộ nội dung bằng prompt bên dưới:
Prompt để cập nhật app/contact/page.tsx:
Viết lại file `app/contact/page.tsx` với Server Action:

1. `'use client'` directive
2. Import `useState` từ 'react'
3. Import `sendContactNotification` từ '@/app/actions/notify'
4. State `pending` (boolean, mặc định false)
5. Hàm async `handleSubmit(formData: FormData)`:
   - setPending(true)
   - const result = await sendContactNotification(formData)
   - setPending(false)
   - if (result.success) alert('Gửi thành công! Chúng tôi sẽ liên hệ sớm.')
   - else alert(result.error || 'Có lỗi xảy ra')
6. Form có: h1 'Liên hệ với chúng tôi', input name (required), input email type=email (required), input phone type=tel, textarea message (required, rows=5), button submit (disabled={pending}, hiển thị pending ? 'Đang gửi...' : 'Gửi liên hệ')
7. Dùng form action={handleSubmit}, className container: max-w-2xl mx-auto p-6 space-y-4
8. Các input dùng className: w-full px-4 py-2 border rounded-lg
9. Button: w-full bg-blue-600 text-white py-3 rounded-lg hover:bg-blue-700 disabled:opacity-50
So sánh nhanh:
API Route (phần 5)Server Action (phần 6)
Số file cần tạo2 file (route + page)1 file (chỉ page)
Cách gửi dữ liệuDùng fetch gửi JSONGửi trực tiếp FormData
Dùng cho ứng dụng bên ngoài được không?✅ Có❌ Không
Độ phức tạpCao hơnĐơn giản hơn

7. Trang trí tin nhắn đẹp hơn

7.1. Tin nhắn với định dạng HTML

Ý tưởng đằng sau

Tin nhắn thuần văn bản trông đơn điệu. Telegram cho phép bạn tô điểm tin nhắn bằng các thẻ đặc biệt — giống như dùng bold, italic trong Word.

Cách làm từng bước

Bước 1 — Mở file lib/telegram.ts Bước 2 — Thêm hàm gửi tin có định dạng Copy prompt bên dưới, paste vào cuối file lib/telegram.ts:
Prompt để thêm vào lib/telegram.ts:
Thêm vào file `lib/telegram.ts` một hàm async `sendFormattedMessage(text: string): Promise<TelegramResponse>`:

- Lấy token từ process.env.TELEGRAM_BOT_TOKEN, chatId từ process.env.TELEGRAM_CHAT_ID
- Nếu thiếu throw Error 'Thiếu TELEGRAM_BOT_TOKEN hoặc TELEGRAM_CHAT_ID'
- fetch POST đến https://api.telegram.org/bot${token}/sendMessage
- Body: { chat_id: chatId, text, parse_mode: 'HTML', cache: 'no-store' }
- Parse response.json(), nếu !data.ok throw Error(data.description)
- Return data
Bước 3 — Sử dụng hàm với các thẻ định dạng Bước 2 — Nhờ AI viết đoạn gọi hàm
Prompt để gọi hàm sendFormattedMessage:
Viết một đoạn TypeScript gọi hàm sendFormattedMessage với nội dung HTML gồm:
- Tiêu đề 🛒 Đơn hàng mới (dùng thẻ <b>)
- Dòng <i>Tên khách:</i> Nguyễn Văn A
- Dòng <b>Giá trị:</b> 1.200.000đ (dùng <s> cho giá cũ gạch ngang, <u> cho giá mới)
- Dòng <code>Mã vận đơn: VN123456</code>
- Dòng link: <a href="https://shop.com/orders/DH-001">👀 Xem chi tiết</a>
Bảng tra cứu các thẻ HTML:
ThẻTác dụngVí dụ
<b>...</b>Chữ đậm<b>Xin chào</b>Xin chào
<i>...</i>Chữ nghiêng<i>Khẩn cấp</i>Khẩn cấp
<u>...</u>Chữ gạch chân<u>Đã xác nhận</u> → Đã xác nhận
<s>...</s>Chữ gạch ngang<s>1.500.000đ</s>1.500.000đ
<code>...</code>Chữ giống codeGiá trị số
<a href="link">text</a>Tạo đường linkNút bấm trong tin nhắn

7.2. Gửi tin nhắn kèm nút bấm

Ý tưởng đằng sau

Bạn có thể thêm nút bấm ngay trong tin nhắn Telegram. Người nhận bấm nút → mở trang web, hoặc gửi phản hồi lại cho bot.

Cách làm từng bước

Bước 1 — Thêm hàm gửi kèm nút bấm Paste prompt bên dưới vào cuối file lib/telegram.ts:
Prompt để thêm vào lib/telegram.ts:
Thêm vào file `lib/telegram.ts` hàm async `sendMessageWithButtons(text: string, buttons: Array<Array<{ text: string; url?: string; callback_data?: string }>>): Promise<TelegramResponse>`:

- Lấy token từ process.env.TELEGRAM_BOT_TOKEN, chatId từ process.env.TELEGRAM_CHAT_ID
- Nếu thiếu throw Error 'Thiếu TELEGRAM_BOT_TOKEN hoặc TELEGRAM_CHAT_ID'
- fetch POST đến https://api.telegram.org/bot${token}/sendMessage
- Body: { chat_id: chatId, text, parse_mode: 'HTML', reply_markup: { inline_keyboard: buttons }, cache: 'no-store' }
- Parse response.json(), nếu !data.ok throw Error(data.description)
- Return data
Bước 2 — Cách sắp xếp nút Nút được sắp xếp theo hàng. Mỗi hàng là một mảng [].
Số hàngCách viếtKết quả
1 hàng, 2 nút[[A, B]][A] [B]
2 hàng, 1 nút mỗi hàng[[A], [B]][A][B]
1 hàng, 3 nút[[A, B, C]][A] [B] [C]
Bước 3 — Nhờ AI viết đoạn gọi hàm
Prompt để gọi hàm sendMessageWithButtons:
Viết một đoạn TypeScript gọi hàm sendMessageWithButtons với:
- text: chuỗi HTML '<b>🛒 Đơn hàng mới #DH-123</b>' + thông tin khách hàng và tổng tiền
- buttons: mảng 2 hàng — hàng 1 gồm 2 nút ['👀 Xem chi tiết' (url đến shop.com/orders/DH-123), '✅ Xác nhận đơn' (callback_data: 'confirm_DH-123')], hàng 2 gồm 1 nút ['❌ Hủy đơn' (callback_data: 'cancel_DH-123')]
Nút url → Mở link trong trình duyệt khi bấm. Nút callback_data → Gửi phản hồi về cho bot (cần lập trình thêm phần xử lý phản hồi).

8. Gửi hình ảnh và file đính kèm

8.1. Gửi hình ảnh

Ý tưởng đằng sau

Thay vì mô tả sản phẩm bằng chữ, bạn có thể gửi kèm hình ảnh trực tiếp trong tin nhắn. Telegram hỗ trợ gửi ảnh từ một đường link URL.

Cách làm từng bước

Bước 1 — Thêm hàm gửi ảnh vào lib/telegram.ts
Prompt để thêm vào lib/telegram.ts:
Thêm vào file `lib/telegram.ts` hàm async `sendPhoto(photoUrl: string, caption?: string): Promise<TelegramResponse>`:

- Lấy token từ process.env.TELEGRAM_BOT_TOKEN, chatId từ process.env.TELEGRAM_CHAT_ID
- Nếu thiếu throw Error 'Thiếu TELEGRAM_BOT_TOKEN hoặc TELEGRAM_CHAT_ID'
- fetch POST đến https://api.telegram.org/bot${token}/sendPhoto
- Body: { chat_id: chatId, photo: photoUrl, caption: caption, parse_mode: 'HTML' }
- Parse response.json(), nếu !data.ok throw Error(data.description)
- Return data
Bước 2 — Nhờ AI viết đoạn gọi hàm
Prompt để gọi hàm sendPhoto:
Viết một đoạn TypeScript gọi hàm sendPhoto với URL ảnh sản phẩm và caption HTML gồm: tên sản phẩm 💻 Laptop HP 15s, giá 14.500.000đ, trạng thái 📦 Còn hàng
Lưu ý: Link ảnh phải là link công khai (public URL). Ảnh từ Google Drive, Dropbox (link chia sẻ) sẽ không gửi được.

8.2. Gửi file tài liệu (PDF, Excel, Word)

Cách làm từng bước

Bước 1 — Thêm hàm gửi file vào lib/telegram.ts
Prompt để thêm vào lib/telegram.ts:
Thêm vào file `lib/telegram.ts` hàm async `sendDocument(fileUrl: string, caption?: string): Promise<TelegramResponse>`:

- Lấy token từ process.env.TELEGRAM_BOT_TOKEN, chatId từ process.env.TELEGRAM_CHAT_ID
- Nếu thiếu throw Error 'Thiếu TELEGRAM_BOT_TOKEN hoặc TELEGRAM_CHAT_ID'
- fetch POST đến https://api.telegram.org/bot${token}/sendDocument
- Body: { chat_id: chatId, document: fileUrl, caption: caption }
- Parse response.json(), nếu !data.ok throw Error(data.description)
- Return data
Bước 2 — Nhờ AI viết đoạn gọi hàm
Prompt để gọi hàm sendDocument:
Viết một đoạn TypeScript gọi hàm sendDocument 2 lần:
1. Gửi file báo cáo Excel URL: https://shop.com/reports/daily-2024-01-15.xlsx, caption: 📊 Báo cáo doanh thu ngày 15/01/2024, Tổng đơn: 45, Doanh thu: 125.000.000đ
2. Gửi file hợp đồng PDF URL: https://shop.com/contracts/HD-2024-001.pdf, caption: 📄 Hợp đồng #HD-2024-001, Khách hàng: Công ty ABC

9. Xử lý lỗi thường gặp

9.1. Các lỗi phổ biến và cách khắc phục

Mã lỗiNghĩaNguyên nhân thường gặpCách xử lý
400 Bad RequestSai cú phápchat_id không đúng định dạng, hoặc thiếu tham số bắt buộcKiểm tra lại chat_id trong file .env.local
401 UnauthorizedKhông xác thực đượcToken bị sai, hoặc bị BotFather thu hồiVào BotFather, dùng lệnh /token để lấy lại token mới
403 ForbiddenKhông có quyềnBot bị user chặn, hoặc bot chưa được thêm vào nhómThêm bot vào nhóm với quyền admin
429 Too Many RequestsGửi quá nhanhGửi hơn 1 tin nhắn/giây đến cùng một chatChờ 1-2 giây giữa mỗi lần gửi

9.2. Giới hạn tốc độ gửi (Rate Limit)

Telegram giới hạn số lần gửi để tránh spam:
Tình huốngGiới hạn
Gửi đến nhiều người khác nhauTối đa 30 tin/giây
Gửi đến cùng một ngườiTối đa 1 tin/giây
Gửi đến cùng một nhómTối đa 20 tin/phút
Thực tế: Với website thông thường, bạn sẽ không bao giờ vượt quá giới hạn này. Chỉ cần lưu ý khi gửi nhiều tin nhắn liên tiếp trong vòng lặp.

9.3. Gửi lại tự động khi thất bại

Ý tưởng đằng sau

Đôi khi mạng chậm hoặc Telegram tạm thời quá tải, tin nhắn gửi thất bại. Thay vì bỏ lỡ, bạn nên thử gửi lại — lần đầu chờ 1 giây, lần sau 2 giây, lần sau nữa 4 giây.

Cách làm từng bước

Bước 1 — Thêm hàm retry vào lib/telegram.ts
Prompt để thêm vào lib/telegram.ts:
Thêm vào file `lib/telegram.ts` hàm async `sendWithRetry(options: SendMessageOptions, maxRetries = 3): Promise<TelegramResponse>`:

- Loop từ 0 đến maxRetries - 1
- Trong try: return await sendTelegramMessage(options)
- Trong catch: nếu là lần thử cuối (attempt === maxRetries - 1) → throw error
- Ngược lại: tính delay = Math.pow(2, attempt) * 1000 (tức 1s, 2s, 4s)
- console.log thông báo đang thử lại
- await new Promise(resolve => setTimeout(resolve, delay))
- Sau vòng loop throw Error 'Đã hết số lần thử lại'
Bước 2 — Nhờ AI cập nhật file
Prompt để thay thế lời gọi hàm:
Tìm trong file app/actions/notify.ts (hoặc app/api/notify/route.ts) dòng await sendTelegramMessage({ text }) và thay bằng await sendWithRetry({ text })
Giải thích đơn giản:
Lần thửThời gian chờ
Thử 1Thất bại → chờ 1 giây
Thử 2Thất bại → chờ 2 giây
Thử 3Thất bại → chờ 4 giây
Thử 4Vẫn lỗi → báo lỗi cho khách

10. Bài tập thực hành

Bài tập 1: Form đăng ký nhận bản tin

Mục tiêu: Tạo trang /subscribe để khách đăng ký nhận email bản tin. Các bước thực hiện:
  1. Tạo thư mục app/subscribe và file page.tsx bên trong
  2. Copy prompt bên dưới vào Claude Code/ChatGPT để tạo file:
Prompt để tạo app/subscribe/page.tsx:
Viết file `app/subscribe/page.tsx` là trang đăng ký nhận bản tin:

1. 'use client'
2. State: email (string), pending (boolean), message (string) — khởi tạo rỗng/false
3. Import `sendContactNotification` từ '@/app/actions/notify'
4. Hàm handleSubmit(e): e.preventDefault(), tạo FormData với email, name='Người đăng ký bản tin', message chứa email, gọi sendContactNotification, cập nhật message thành công/thất bại
5. UI: h1 '📬 Đăng ký nhận bản tin', form có input email (type=email, required, placeholder 'Nhập email của bạn'), button 'Đăng ký' (disabled={pending})
6. Hiển thị message bên dưới form
7. Container: max-w-md mx-auto p-6
8. Các input: w-full px-4 py-2 border rounded-lg
9. Button: w-full bg-blue-600 text-white py-2 rounded-lg hover:bg-blue-700 disabled:opacity-50
  1. Chạy npm run dev → mở http://localhost:3000/subscribe → điền email và bấm Đăng ký
  2. Kiểm tra Telegram — bạn sẽ nhận được tin nhắn thông báo

Bài tập 2: Thông báo đơn hàng với nút bấm

Mục tiêu: Tạo trang /checkout giả lập đặt hàng, gửi thông báo kèm nút bấm. Hướng dẫn:
  1. Tạo thư mục app/checkout và file page.tsx
  2. Copy prompt bên dưới để tạo file:
Prompt để tạo app/checkout/page.tsx:
Viết file `app/checkout/page.tsx` là trang giả lập checkout:

1. 'use client'
2. State: ordered (boolean, mặc định false)
3. Import `sendMessageWithButtons` từ '@/lib/telegram'
4. Hàm handleOrder: tạo orderId = 'DH-' + random 6 ký tự, total = 1_450_000, gọi sendMessageWithButtons với message HTML có: icon 🛒, mã đơn hàng, thông tin khách, icon 💰 tổng tiền dùng toLocaleString('vi-VN'), nút bấm: ['👀 Xem chi tiết' (url đến shop.com/orders/{orderId}), '✅ Xác nhận đơn' (callback_data: confirm_{orderId})]
5. Sau khi gọi: setOrdered(true)
6. UI: nếu ordered → hiển thị '🎉 Đặt hàng thành công!'
7. Ngược lại: h1 '🛒 Thanh toán đơn hàng', div thông tin sản phẩm (💻 Laptop HP 15s, Giá: 1.450.000đ), button 'Đặt hàng ngay' (bg-green-600, w-full)
  1. Chạy npm run dev → mở http://localhost:3000/checkout
  2. Bấm Đặt hàng ngay → kiểm tra Telegram

Bài tập 3: Gửi ảnh sản phẩm qua Telegram

Mục tiêu: Thêm hình ảnh vào thông báo đơn hàng. Hướng dẫn:
  1. Mở file lib/telegram.ts, thêm hàm sendPhoto (xem phần 8.1)
  2. Tạo thư mục app/product và file page.tsx bằng prompt:
Prompt để tạo app/product/page.tsx:
Viết file `app/product/page.tsx` với tính năng gửi ảnh sản phẩm qua Telegram:

1. 'use client'
2. Import `sendPhoto` từ '@/lib/telegram'
3. Hàm handleNotify: gọi sendPhoto với URL ảnh 'https://images.unsplash.com/photo-1491553895911-0055uj6e4cef?w=800' và caption HTML '<b>💻 Laptop mới về!</b>\n💰 Giá: 18.900.000đ\n📦 Số lượng: 5 cái', alert thành công
4. UI: h1 'Chi tiết sản phẩm', thẻ img src ảnh đó (w-400, rounded-lg, mb-4), p '💻 Laptop HP 15s — 18.900.000đ', button 'Gửi thông báo Telegram' (bg-blue-600 text-white px-6 py-2 rounded-lg)
  1. Chạy thử và kiểm tra kết quả

Bài tập 4 (nâng cao): Tự động thử lại khi gửi thất bại

Mục tiêu: Cập nhật Server Action để tự động thử lại khi Telegram không phản hồi. Hướng dẫn:
  1. Mở file app/actions/notify.ts
  2. Thêm sendWithRetry từ phần 9.3 vào lib/telegram.ts
  3. Trong file notify.ts, thay sendTelegramMessage bằng sendWithRetry bằng prompt:
Prompt để cập nhật app/actions/notify.ts:
Cập nhật file `app/actions/notify.ts`:

1. Thêm import `sendWithRetry` từ '@/lib/telegram' (thay vì sendTelegramMessage)
2. Thay await sendTelegramMessage({ text }) bằng await sendWithRetry({ text })
3. Catch block: return { success: false, error: 'Gửi thất bại sau nhiều lần thử' }

11. Tổng kết

Những điểm cần nhớ

  • Token Telegram phải được bảo mật, không bao giờ đặt ở client-side.
  • Luôn validate và escape input của user trước khi gửi.
  • Sử dụng API Route hoặc Server Action — đừng gọi Telegram API trực tiếp từ browser.
  • Hiểu rõ rate limit để tránh bot bị Telegram block tạm thời.
  • HTML parse mode dễ dùng hơn Markdown vì ít ký tự cần escape.

Tài liệu tham khảo

Hướng phát triển tiếp theo

Sau khi đã thành thạo bài học này, học viên có thể tìm hiểu sâu hơn về:
  • Webhook: Nhận tin nhắn 2 chiều thay vì chỉ gửi ra.
  • Telegraf.js: Framework xây dựng bot Telegram phức tạp với Node.js.
  • Inline Mode: Cho phép user gọi bot từ bất kỳ chat nào với cú pháp @your_bot query.
  • Payments API: Tích hợp thanh toán trực tiếp trong Telegram.
  • Mini Apps: Xây dựng web app chạy bên trong Telegram.

Bài giảng được biên soạn cho khóa học tại Softech Aptech Đà Nẵng.