iT邦幫忙

2025 iThome 鐵人賽

DAY 14
0
IT 管理

無職轉生:到了IT就拿出真本事!IT 維運 30 日修煉系列 第 14

Day 14 - 章節十三:Gmail 守護者 ‧ GAS 郵件監控術與異常預警系統(自動化篇)

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20250914/20178480DVHRGFvjsd.png

🌀 事件起源

某天,客服突然接到客戶抱怨:同一封郵件一口氣重複收到好幾次

一開始以為只是單一案例,沒想到很快就有更多人反映「信箱被洗版」,重要郵件反而被淹沒──這不僅讓客戶不堪其擾,也讓我們懷疑系統是不是被什麼「程式詛咒」纏上了。

為了避免這樣的怪異現象繼續擴散,IT 工程師必須設下一道「封印」,即時偵測郵件是否異常重複發送,並在第一時間觸發警報,守住信件傳遞的最後防線。


⚡ 解法思路

為了防止「郵件暴走」,設計了一個 Google Apps Script 監測規則

  • 每分鐘檢查一次已發送郵件
  • 🔎 搜尋最近 50 封 Mail,確保即時掌握
  • 👀 檢查是否有同一收件者超過兩封以上
  • 🚨 若超標,立即發送 Alert 到 Google Chat
  • 📝 把已通知過的收件者存入清單,避免一直重複提醒
  • 🔄 每小時自動重置清單,確保下一輪檢查正常運作

🛠 技術封印 ‧ GAS 腳本

📄 PreventDuplicateSend.gs

/**
 * 檢查 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()}`);
  }
}

📄 ResetNotifiedRecipients.gs

/**
 * 重置已通知過的收件者清單
 * 每小時執行一次
 */
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("========================");
}

⏳ 觸發器設定

自動執行設定

  1. checkGmail()每分鐘執行一次

    • 觸發器類型:時間驅動
    • 執行頻率:每分鐘
    • 用途:即時監測郵件重複發送
  2. resetNotifiedRecipients()每小時執行一次

    • 觸發器類型:時間驅動
    • 執行頻率:每小時
    • 用途:重置通知清單,避免誤判

設定步驟

// 在 Google Apps Script 編輯器中:
// 1. 點選左側「觸發條件」圖示
// 2. 點選「+ 新增觸發條件」
// 3. 設定如下:

// 觸發器 1:即時監測
// 選擇要執行的功能:checkGmail
// 選擇活動來源:時間驅動
// 選擇時間類型:分鐘計時器
// 選擇分鐘間隔:每分鐘

// 觸發器 2:清單重置
// 選擇要執行的功能:resetNotifiedRecipients
// 選擇活動來源:時間驅動
// 選擇時間類型:小時計時器
// 選擇小時間隔:每小時

這樣就能即時防止暴走郵件,並且保證警示不會過度觸發。


✅ 測試結果

模擬測試環境

在模擬環境中,當程式異常持續發送相同郵件時,GAS 成功攔截異常:

  • 收件者信箱出現超過 2 封郵件,立即觸發 Chat Alert
  • 清單記錄機制,避免重複提醒造成警報疲勞
  • 每小時自動歸零,確保下一輪檢查環境乾淨
  • 即時監測,異常發生後 1 分鐘內就能收到通知

實際運行效果

⚠️ 郵件異常警報!
收件者: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:權限不足

  • 確認 Gmail API 權限已授權
  • 檢查 PropertiesService 讀寫權限
  • 驗證 UrlFetchApp 外部連線權限

📖 小結

這個案例證明,Google Apps Script 不只是小工具,它可以是團隊 IT 系統的守護結界

透過一點巧思,我們能在最短時間設下「監測防線」,防止災難擴大:

  • 🛡️ 即時防護:每分鐘監測,異常立即警報
  • 🎯 精準識別:準確偵測重複郵件模式
  • 🔄 智慧管理:避免警報疲勞的清單機制
  • 📊 系統化:完整的日誌與報表功能
  • 🚀 零成本:利用 Google 生態系統,無需額外費用

當系統出現「程式詛咒」時,這套封印術能確保我們在第一時間發現問題,保護客戶體驗,維護企業信譽。



上一篇
Day 13 - 章節十二:Gemini API × GAS 問卷工坊 ‧ 自動生成智慧表單(AI 修練篇)
系列文
無職轉生:到了IT就拿出真本事!IT 維運 30 日修煉14
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言