20.4.26 |
25
nhận xét
|
lượt xem
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!












ODljZzJhN2s2
REPLY DELETEtuyệt vời a 😘
REPLY DELETEcm9lbGdsY3J1
REPLY DELETECộng đồng blogspot ru nhau ngủ hết rồi à?
REPLY DELETEKhông có giá trị gì cho cuộc sống của họ nên họ không sử dụng thôi. Khi sở thích không nuôi sống được gia đình, bản thân
REPLY DELETENhờ a giúp hỗ trợ xữ lý lỗi này giùm e.
REPLY DELETEhttps://i.ibb.co/v6x2jBZM/robot.png
Bị như thế này là bị sao a? e dùng cloudflare để quản lý domain.
Em cảm ơn a
Em tắt hết mấy cái này đi xem sao
REPLY DELETE- Rocket Loader
- Auto Minify JS
- Defer JavaScript
Này do cài js nào đó vào mới bị chứ captcha của cloudflare nó khác ko phải của gg
REPLY DELETENhận xét này đã bị quản trị viên blog xóa.
REPLY DELETEAnh em Blogspot lại để cho anh em WordPress vượt qua mặt rồi!
REPLY DELETEĐã bao giờ vượt mặt được WP đâu mà "lại". Dùng free làm sao hơn được cái mất phí 🤣🤣🤣
REPLY DELETEngoài nút like và nút dislike ra thì thêm nút haha là hợp lý Bro
REPLY DELETENgại lắm 🤣
REPLY DELETEAnh em mình cứ dùng blogspot là ngon rồi, bên wordpress toàn dính mấy vụ plugin dính virus các thứ.
REPLY DELETECũng giống kiểu thích dùng đồ cr@ck vậy 😁
REPLY DELETECái này giống bản gốc nhỉ admin? https://www.blogspotvi.net/
REPLY DELETEHay anh em tạo nhóm chat trên telegram cộng đồng blogspot chia sẻ kinh nghiệm hay hỗ trợ cho nhau nói chuyện cho rôm rả đi, chứ cộng đồng mất kết nối với nhau quá.
REPLY DELETEThường họ dùng discord 😁
REPLY DELETEGiờ mới biết blogspot là 1 trong những công cụ để ba que tuyên truyền chống phá đảng.
REPLY DELETELũ rác rưởi não lợn đấy có tí sức mạnh nào đâu, khỏi quan tâm 😌
REPLY DELETENhưng mà nhìn ngứa mắt chẳng làm được gì, vô cmt sợ chúng nó tấn công acc google mình bị bay. Chúng nó lập nhiều blog vãi luôn, đến nỗi nhà mạng VN chặn không hết được tụi nó mà blog toàn đc index lên kết quả tìm kiếm.
REPLY DELETECó cách nào để gg index bài viết với tốc độ siêu nhanh ntn không nhỉ? https://lh3.googleusercontent.com/d/1D8QtlQuPgrfzUMIRhan5sE8LXc9Ns0RF=w1000
REPLY DELETEEm đang nghiên cứu hệ thống quản lý blogspot dạng nâng cao trong cũng oke phết: https://i.imgur.com/8s2pOF5.jpeg
REPLY DELETEChúc anh trai đầu tuần vui vẻ, mọi việc hanh thông thuận lời ❤️
REPLY DELETEChúc anh em cuối tuần làm việc hiệu quả, các chú cẩn thận vào hè là học sinh sinh viên lập hàng loạt blog để viết đó.
REPLY DELETE