某天,客服突然接到客戶抱怨:同一封郵件一口氣重複收到好幾次。
一開始以為只是單一案例,沒想到很快就有更多人反映「信箱被洗版」,重要郵件反而被淹沒──這不僅讓客戶不堪其擾,也讓我們懷疑系統是不是被什麼「程式詛咒」纏上了。
為了避免這樣的怪異現象繼續擴散,IT 工程師必須設下一道「封印」,即時偵測郵件是否異常重複發送,並在第一時間觸發警報,守住信件傳遞的最後防線。
為了防止「郵件暴走」,設計了一個 Google Apps Script 監測規則:
/**
* 檢查 Gmail 是否有重複發送郵件
* 每分鐘執行一次
*/
function checkGmail() {
// 搜尋已發送的特定郵件
const searchQuery = 'in:sent "Your Gift certificate has been sent"';
const maxResults = 50;
const threads = GmailApp.search(searchQuery, 0, maxResults);
const recipientCount = {};
// 取得已通知過的收件者清單
const properties = PropertiesService.getScriptProperties();
const notifiedRecipients = properties.getProperty('notifiedRecipients');
const notifiedSet = notifiedRecipients ? new Set(notifiedRecipients.split(',')) : new Set();
// 計算每個收件者的郵件數量
threads.forEach(thread => {
const messages = thread.getMessages();
messages.forEach(message => {
const recipient = message.getTo();
recipientCount[recipient] = (recipientCount[recipient] || 0) + 1;
});
});
// 檢查是否有異常重複發送
for (const recipient in recipientCount) {
if (recipientCount[recipient] > 2 && !notifiedSet.has(recipient)) {
sendChatNotification(recipient, recipientCount[recipient]);
notifiedSet.add(recipient);
}
}
// 更新已通知的收件者清單
properties.setProperty('notifiedRecipients', Array.from(notifiedSet).join(','));
}
/**
* 發送 Google Chat 警報通知
* @param {string} recipient - 收件者信箱
* @param {number} count - 郵件重複數量
*/
function sendChatNotification(recipient, count) {
const url = 'YOUR_CHAT_WEBHOOK_URL';
const payload = {
"text": `⚠️ 郵件異常警報!\n收件者:${recipient}\n重複郵件數量:${count} 封\n請立即檢查發送系統狀態!`
};
const options = {
method: 'post',
contentType: 'application/json',
payload: JSON.stringify(payload)
};
try {
UrlFetchApp.fetch(url, options);
Logger.log(`✅ 已發送警報:${recipient} 收到 ${count} 封重複郵件`);
} catch (error) {
Logger.log(`❌ 警報發送失敗:${error.toString()}`);
}
}
/**
* 重置已通知過的收件者清單
* 每小時執行一次
*/
function resetNotifiedRecipients() {
const properties = PropertiesService.getScriptProperties();
properties.deleteProperty('notifiedRecipients');
Logger.log("✅ 已重置已通知過的收件者清單");
}
/**
* 手動測試功能 - 模擬郵件重複發送
*/
function testDuplicateDetection() {
Logger.log("🧪 開始測試重複郵件偵測功能...");
// 執行檢查
checkGmail();
// 顯示當前狀態
const properties = PropertiesService.getScriptProperties();
const notifiedRecipients = properties.getProperty('notifiedRecipients');
Logger.log("📋 當前已通知清單:" + (notifiedRecipients || "無"));
}
/**
* 查看系統狀態
*/
function showSystemStatus() {
const properties = PropertiesService.getScriptProperties();
const notifiedRecipients = properties.getProperty('notifiedRecipients');
Logger.log("=== 郵件監測系統狀態 ===");
Logger.log("📊 已通知收件者:" + (notifiedRecipients || "無"));
Logger.log("⏰ 系統時間:" + new Date().toLocaleString('zh-TW'));
Logger.log("========================");
}
checkGmail() → 每分鐘執行一次
resetNotifiedRecipients() → 每小時執行一次
// 在 Google Apps Script 編輯器中:
// 1. 點選左側「觸發條件」圖示
// 2. 點選「+ 新增觸發條件」
// 3. 設定如下:
// 觸發器 1:即時監測
// 選擇要執行的功能:checkGmail
// 選擇活動來源:時間驅動
// 選擇時間類型:分鐘計時器
// 選擇分鐘間隔:每分鐘
// 觸發器 2:清單重置
// 選擇要執行的功能:resetNotifiedRecipients
// 選擇活動來源:時間驅動
// 選擇時間類型:小時計時器
// 選擇小時間隔:每小時
這樣就能即時防止暴走郵件,並且保證警示不會過度觸發。
在模擬環境中,當程式異常持續發送相同郵件時,GAS 成功攔截異常:
⚠️ 郵件異常警報!
收件者:customer@example.com
重複郵件數量:5 封
請立即檢查發送系統狀態!
[2025-09-13 16:15:00] ✅ 已發送警報:customer@example.com 收到 5 封重複郵件
[2025-09-13 16:16:00] 📊 監測中...無異常
[2025-09-13 17:00:00] ✅ 已重置已通知過的收件者清單
[2025-09-13 17:01:00] 📊 監測中...無異常
就這樣,郵件暴走的危機被化解!
// 可調整的監測參數
const CONFIG = {
searchQuery: 'in:sent "Your Gift certificate has been sent"', // 搜尋條件
maxResults: 50, // 檢查郵件數量
alertThreshold: 2, // 警報觸發門檻
checkInterval: 1, // 檢查間隔(分鐘)
resetInterval: 60 // 重置間隔(分鐘)
};
/**
* 多管道警報通知
*/
function sendMultiChannelAlert(recipient, count) {
// Google Chat 通知
sendChatNotification(recipient, count);
// Email 通知給管理員
sendEmailAlert(recipient, count);
// Slack 通知(可選)
sendSlackAlert(recipient, count);
}
function sendEmailAlert(recipient, count) {
const subject = `🚨 郵件系統異常警報 - ${recipient}`;
const body = `
系統偵測到郵件重複發送異常:
收件者:${recipient}
重複數量:${count} 封
偵測時間:${new Date().toLocaleString('zh-TW')}
請立即檢查郵件發送系統狀態。
`;
GmailApp.sendEmail('admin@yourcompany.com', subject, body);
}
/**
* 生成每日監測報表
*/
function generateDailyReport() {
const properties = PropertiesService.getScriptProperties();
const alertHistory = properties.getProperty('alertHistory') || '[]';
const alerts = JSON.parse(alertHistory);
// 生成報表內容
const reportContent = alerts.map(alert =>
`${alert.time} - ${alert.recipient} (${alert.count} 封)`
).join('\n');
// 發送報表
GmailApp.sendEmail(
'admin@yourcompany.com',
'📊 每日郵件監測報表',
`今日郵件異常紀錄:\n\n${reportContent || '無異常'}`
);
// 清除歷史紀錄
properties.deleteProperty('alertHistory');
}
問題 1:觸發器沒有正常執行
// 檢查觸發器狀態
function checkTriggers() {
const triggers = ScriptApp.getProjectTriggers();
Logger.log(`目前有 ${triggers.length} 個觸發器`);
triggers.forEach((trigger, index) => {
Logger.log(`觸發器 ${index + 1}: ${trigger.getHandlerFunction()}`);
});
}
問題 2:Google Chat Webhook 無法發送
// 測試 Webhook 連線
function testWebhook() {
try {
sendChatNotification('test@example.com', 999);
Logger.log('✅ Webhook 測試成功');
} catch (error) {
Logger.log('❌ Webhook 測試失敗:' + error.toString());
}
}
問題 3:權限不足
這個案例證明,Google Apps Script 不只是小工具,它可以是團隊 IT 系統的守護結界。
透過一點巧思,我們能在最短時間設下「監測防線」,防止災難擴大:
當系統出現「程式詛咒」時,這套封印術能確保我們在第一時間發現問題,保護客戶體驗,維護企業信譽。