Hướng dẫn tạo chức năng nhận bài viết mới qua Email
"Hướng dẫn tạo chức năng nhận bài viết mới qua email mà không phụ thuộc bên thứ 3 và chủ động 100% dữ liệu"
9 min read
Mình vừa hoàn thiện xong một tính năng khá hay cho blog: nhận thông báo bài viết mới qua email mà không cần dùng dịch vụ bên thứ 3 như FeedBurner.
Hôm nay mình chia sẻ lại toàn bộ cách làm để anh em có thể tự triển khai.
Hơi dài và loằng ngoằng nhưng anh em chịu khó thực hiện theo đúng hướng dẫn nhé
Flow đơn giản
- Người dùng nhập email đăng ký
- Hệ thống gửi link xác nhận
- Người dùng bấm xác nhận → lưu vào database
- Khi có bài viết mới → gửi email hàng loạt
Công nghệ áp dụng
- Cloudflare Workers
- KV Storage
- Apps Script
Demo
Thực hiện
Tạo Cloudflare Worker
Bước 1: Vào Cloudflare → Compute → Workers & Pages → Create application → Chọn "Start with Hello World!" → Deploy
Bước 2: Edit code và Dán code sau:
- Ghi nhớ link của worker này. Có dạng subscribe.vncoding.workers.dev, trong đó subscribe là tên bạn đã đặt cho dự án này để thực hiện cho bước này
- Nhớ thay thế https://www.vncoding.com thành link blog của bạn
- Đặc biệt bạn sẽ phải tạo 1 page và đặt tên là confirm để có link dạng https://www.vncoding.com/p/confirm.html, phục vụ cho bước này
Tạo KV namespace
Cloudflare → Storage & databases → Workers KV → Create Instance → Sau đó tạo KV tên như sauSUB_KV
Tạo page confirm
Trong blogspot tạo 1 page mới và dán code sau:
<div style="margin:auto;text-align:center;">
<!-- TITLE -->
<h1 style="margin:10px 0;color:#333;">Kiểm tra email của bạn</h1>
<!-- DESC -->
<p style="color:#666;line-height:1.6;">
Chúng tôi đã gửi link xác nhận đăng ký.<br>
Vui lòng kiểm tra hộp thư và nhấn vào liên kết để hoàn tất.
</p>
<!-- EMAIL -->
<p id="emailBox" style="margin:15px 0;font-weight:bold;color:#333;"></p>
<!-- NOTE -->
<div style="margin-top:20px;padding:12px;background:#f1f3f5;border-radius:8px;font-size:13px;color:#777;">
Không thấy email? Hãy kiểm tra thư rác (Spam) hoặc Quảng cáo (Promotions).
</div>
<!-- BUTTON -->
<a href="https://mail.google.com" target="_blank"
style="display:inline-block;margin-top:20px;padding:12px 20px;background: linear-gradient(135deg, #005b53, #80ada9);color:#fff;text-decoration:none;border-radius:8px;font-weight:bold;">
📬 Mở Gmail
</a>
<!-- BACK -->
<div style="margin-top:20px;">
<a href="https://www.vncoding.com" style="font-size:13px;color:#999;text-decoration:none;">
← Quay về trang chủ
</a>
</div>
</div>
<script>
(function(){
const email = new URL(location.href).searchParams.get("email");
if (email) {
document.getElementById("emailBox").innerHTML =
"Email: <span style='color:#000'>" + decodeURIComponent(email) + "</span>";
}
})();
</script>
Google Apps Script
Bước 1: Vào Apps Script → Dự Án Mới → Đặt tên dự án là MAIL_GAS → Dán đoạn code sau vào nội dung của Mã.gsfunction doGet() {
return ContentService.createTextOutput("OK");
}
function doPost(e) {
var data = {};
try {
data = JSON.parse(e.postData.contents);
} catch (err) {
return ContentService.createTextOutput("Lỗi JSON");
}
Logger.log(data);
// ===== CONFIRM =====
if (data.type === "confirm") {
var link = "https://subscribe.vncoding.workers.dev/confirm?token=" + data.token;
GmailApp.sendEmail(
data.email,
"Xác nhận đăng ký VNCoding",
"Click: " + link,
{
htmlBody: `
<div style="font-family:Arial;background:#f5f7fa;padding:20px;">
<div style="max-width:600px;margin:auto;background:#fff;padding:30px;border-radius:12px;box-shadow:0 8px 25px rgba(0,0,0,0.08);text-align:center;">
<!-- LOGO -->
<img src="https://www.vncoding.com/favicon.ico" style="width:50px;margin-bottom:15px;">
<!-- TITLE -->
<h2 style="margin:10px 0;color:#333;">Xác nhận đăng ký</h2>
<!-- TEXT -->
<p style="color:#666;line-height:1.6;">
Bạn vừa đăng ký nhận thông báo bài viết mới từ <b>VNCoding</b>.<br>
Nhấn nút bên dưới để hoàn tất đăng ký.
</p>
<!-- BUTTON -->
<a href="${link}"
style="display:inline-block;margin:20px 0;padding:14px 24px;background:linear-gradient(135deg,#4facfe,#00f2fe);color:#fff;text-decoration:none;border-radius:8px;font-weight:bold;">
✔ Xác nhận đăng ký
</a>
<!-- FALLBACK -->
<p style="font-size:13px;color:#999;">
Nếu nút không hoạt động, hãy copy link bên dưới:
</p>
<div style="word-break:break-all;font-size:12px;color:#666;background:#f1f1f1;padding:10px;border-radius:6px;">
${link}
</div>
<!-- FOOTER -->
<p style="font-size:12px;color:#aaa;margin-top:20px;">
Nếu bạn không yêu cầu đăng ký, hãy bỏ qua email này.
</p>
</div>
</div>
`
}
);
}
// ===== NEW POST =====
if (data.type === "newpost" && data.emails) {
// FIX UTF-8 SUBJECT
var subject = Utilities.newBlob("Bài viết mới từ VNCoding: " + data.title).getDataAsString("UTF-8");
data.emails.forEach(email => {
var unsub = "https://subscribe.vncoding.workers.dev/unsubscribe?email=" + encodeURIComponent(email);
GmailApp.sendEmail(
email,
subject,
data.description || "",
{
htmlBody: `
<div style="font-family:Arial;background:#f5f7fa;padding:20px;">
<div style="max-width:600px;margin:auto;background:#fff;padding:25px;border-radius:10px;box-shadow:0 5px 15px rgba(0,0,0,0.05);">
<!-- TITLE -->
<h2 style="color:#222;margin-top:0;">${data.title}</h2>
<!-- THUMBNAIL -->
${data.thumbnail ? `
<img src="${data.thumbnail}"
style="width:100%;border-radius:8px;margin:10px 0;">
` : ""}
<!-- DESCRIPTION -->
<p style="color:#555;line-height:1.6;">
${data.description || "Bài viết mới từ VNCoding đã được đăng."}
</p>
<!-- BUTTON -->
<a href="${data.link}"
style="display:inline-block;margin:15px 0;padding:12px 20px;background:linear-gradient(135deg,#4facfe,#00f2fe);color:#fff;text-decoration:none;border-radius:6px;font-weight:bold;">
Đọc bài ngay
</a>
<hr style="border:none;border-top:1px solid #eee;margin:20px 0;">
<p style="font-size:12px;color:#999;">
Bạn nhận email này vì đã đăng ký nhận bài viết từ VNCoding.
</p>
<a href="${unsub}" style="font-size:12px;color:#ff4d4f;text-decoration:none;">
Hủy đăng ký
</a>
</div>
</div>
`
}
);
});
}
return ContentService.createTextOutput("OK");
}
// ===== AUTO SEND =====
function sendNewPost() {
var props = PropertiesService.getScriptProperties();
var lastId = props.getProperty("lastPostId");
var feed = UrlFetchApp.fetch("https://www.vncoding.com/feeds/posts/default?alt=json");
var data = JSON.parse(feed.getContentText());
var latest = data.feed.entry[0];
var id = latest.id.$t;
if (id === lastId) return;
// ===== FIX UTF-8 =====
var rawTitle = latest.title.$t;
var title = Utilities.newBlob(rawTitle).getDataAsString("UTF-8");
var link = latest.link.find(l => l.rel === "alternate").href;
// ===== LẤY DESCRIPTION =====
var rawContent = latest.content.$t;
var description = rawContent
.replace(/<[^>]*>?/gm, "") // bỏ HTML
.substring(0, 200) + "...";
// ===== LẤY THUMBNAIL =====
var thumbnail = "";
if (latest.media$thumbnail) {
thumbnail = latest.media$thumbnail.url.replace("s72-c", "s600");
} else {
var match = rawContent.match(/<img.*?src="(.*?)"/);
if (match) thumbnail = match[1];
}
// ===== LẤY EMAIL =====
var res = UrlFetchApp.fetch("subscribe.vncoding.workers.dev/get-subs");
var emails = JSON.parse(res.getContentText());
// ===== GỬI MAIL =====
UrlFetchApp.fetch("URL dự án Apps Script", {
method: "POST",
payload: JSON.stringify({
type: "newpost",
emails,
title,
link,
description,
thumbnail
})
});
props.setProperty("lastPostId", id);
}
Lưu ý: Thay toàn bộ https://subscribe.vncoding.com thành link worker của bạn tại bước này
Bước 2: Triển khai → Chọn loại "Ứng dụng web" → Cấu hình như sau
Bước 3:Copy URL sau đó thay thế vào URL dự án Apps Script ở code của "Mã.gs" ở bước trên
Bước 4: Triển khai lại dự án
Triển khai → Quản lý các tùy chọn triển khai → Chỉnh sửa (button hình bút chì) → Triển khai
Bước 5: Cài đặt thời gian check bài mới
Tạo form đăng ký trên blog
<div class="subscribe-box">
<input type="email" id="email" placeholder="Nhập email">
<button onclick="subscribe()">Đăng ký</button>
</div>
<script>
function subscribe(){
const email = document.getElementById("email").value;
fetch("https://subscribe.vncoding.workers.dev/subscribe", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({ email })
})
.then(res => res.json())
.then(data => alert(data.message));
}
</script>
Đừng quên thay thế https://subscribe.vncoding.workers.dev thành link worker dự án này của bạn
Kiểm tra người đăng ký
Tại dự án worker, click chọn KV đã tạo trước đó để xem danh sách các email đã đăng ký nhận bài viết trên blog của bạnKết luận
Như vậy mình đã hướng dẫn anh em tự build hệ thống nhận bài viết mới qua email với tính năng chủ động 100% dữ liệu, Không phụ thuộc bên thứ 3.Anh em thực hiện có gì vướng mắc để lại bình luận bên dưới mình sẽ hỗ trợ. Chúc anh em một ngày vui!










