Gmail:取得草稿 getDrafts()

 



寫信這件事,真正卡人的不是寄出,而是「寄出前」。草稿一多,標題長得像雙胞胎、內容又要改來改去,很容易手滑寄錯人,或是找不到前一天那封「已經改到第 7 版」的草稿。

這篇會帶你把 Gmail 的 getDrafts() 用好用滿:先搞清楚它能抓到什麼資料、有哪些限制,再用幾個貼近日常的實作範例——例如批次列出所有草稿、用關鍵字與時間條件縮小範圍、把草稿同步到試算表、更新收件人與主旨,最後再補上「真正會踩到的雷」和解法。

整套流程走完,你可以把「草稿」當成團隊的寄信前置台:行銷信先放草稿、主管複核打勾才寄、寄出後自動回寫紀錄。少一點手動翻找,多一點可追溯的秩序。希望本篇文章可以幫助到需要的您。   


目錄

{tocify} $title={目錄} 


為什麼要學 getDrafts()?

當你把 Gmail 當成「寄送前置台」——例如行銷信先存成草稿、業務群發前要人工複核、或把草稿當「範本庫」——批次抓取與處理草稿 就成了關鍵。

在 Google Apps Script(GAS)裡,GmailApp.getDrafts() 能一次取得所有草稿(回傳 GmailDraft[]),搭配 GmailDraft / GmailMessage 的方法就能讀主旨、收件者、內文、附件,甚至直接更新或寄出。


getDrafts() 到底做了什麼

做什麼:抓回「目前帳號下的所有草稿」,型別是 GmailDraft 陣列。

能幹嘛:

        用 draft.getId() 取得草稿 ID。

        透過 draft.getMessage() 變成 GmailMessage,可讀 getSubject()、getTo()、    getDate()、getAttachments() 等欄位。

        直接 draft.update() 改草稿內容,或 draft.send() 寄出。


小提醒:getDrafts() 不支援伺服器端查詢條件。若你草稿很多、又想先過濾,建議改用 GmailApp.search('in:drafts ...') 先以 Gmail 搜尋語法縮小範圍,再用 message.isDraft() 篩掉非草稿。搜尋語法等同 Gmail 搜尋盒的 operators(如 subject:、newer_than:、from:)。


開工前的準備

1.    開啟 [script.new] 或在試算表/文件上方選單 擴充功能 → Apps Script 建立 GAS 專案。

2.    第一次執行會跳出授權畫面,因為 GmailApp 需要讀寫 Gmail(敏感範圍)。這是正常的 OAuth 授權流程。

3.    如果要自動跑(排程),請在 觸發條件 設定「可安裝觸發器」(例如時間驅動)。不要用簡單觸發器 onOpen / onEdit 直接碰 Gmail,因為簡單觸發器無法存取需授權的服務。


快速上手:三段最實用的 getDrafts() 範例

範例 1:列出所有草稿的重點資訊

function listAllDrafts() {
  const drafts = GmailApp.getDrafts(); // 取得所有草稿
  drafts.forEach((d, i) => {
    const m = d.getMessage();
    Logger.log(
      `#${i + 1} id=${d.getId()} to=${m.getTo()} subject=${m.getSubject()} date=${m.getDate()}`
    );
  });
}


重點:getMessage() 回傳 GmailMessage,能再取 getTo()、getSubject()、getDate() 等。


範例 2:只抓「近 7 天、主旨含關鍵字」的草稿

function searchRecentDrafts() {
  // 用 Gmail 搜尋語法先縮小,避免一次拉超多資料
  const query = 'in:drafts newer_than:7d subject:"報價"';
  const threads = GmailApp.search(query, 0, 50); // 先抓 50 筆執行一輪
  const results = [];

  threads.forEach(th => {
    th.getMessages().forEach(msg => {
      if (msg.isDraft()) {
        results.push({
          messageId: msg.getId(),
          subject: msg.getSubject(),
          to: msg.getTo(),
          date: msg.getDate()
        });
      }
    });
  });

  Logger.log(JSON.stringify(results, null, 2));
  return results;
}


說明:搜尋語法與 Gmail 相同(in:drafts、newer_than:、subject:)。以 isDraft() 再次確認訊息確為草稿。


範例 3:更新草稿(改收件人/主旨/內文),或直接寄出

function updateAndSendFirstDraft() {
  const draft = GmailApp.getDrafts()[0];
  if (!draft) throw new Error('沒有草稿可更新');

  // 將草稿內容替換成新版本
  draft.update(
    'client@example.com',
    '【更新版】十月報價單',
    '文字版內文',
    {
      name: '業務小幫手',
      htmlBody: '<p>您好,這是十月最新報價,請參考。</p>'
    }
  );

  // 最後一步:真的要寄出才解除註解
  // const sent = draft.send();
  // Logger.log(`已寄出,messageId=${sent.getId()}`);
}


GmailDraft.update() / send() 皆受郵件大小與配額限制(內文大小、附件上限、每日讀寫與寄送等)。


進階:把草稿清單「同步到 Google 試算表」

function syncDraftsToSheet() {
  const ss = SpreadsheetApp.getActive();
  const sh = ss.getSheetByName('Drafts') || ss.insertSheet('Drafts');
  sh.clear().appendRow(['DraftId','To','Subject','Date','HasAttachments','ApproxSize']);

  const drafts = GmailApp.getDrafts();
  drafts.forEach(d => {
    const m = d.getMessage();
    const approxSize = m.getRawContent().length; // 粗估大小(含標頭)
    sh.appendRow([
      d.getId(),
      m.getTo(),
      m.getSubject(),
      m.getDate(),
      m.getAttachments().length > 0,
      approxSize
    ]);
  });
}


備註:getRawContent() 讀原始信件內容,用來粗估大小,請酌量使用(資料量大會耗時)。


常見錯誤與雷點

1.    怎麼回傳空陣列?

    可能原因

        帳號真的沒有草稿。

        你在代管或委派情境(存取的不是原草稿擁有者)。

        程式其實跑在另一個帳號(例如共用試算表裡不同擁有者)。

    修正

        先手動確認目前登入的 Gmail 是否看得到草稿。

        確認專案擁有者與觸發器執行身分。

2.    觸發器報權限不足(Authorization required/缺授權)

    症狀:用 onOpen / onEdit 跑 Gmail 會直接噴錯。

    原因:簡單觸發器 無法呼叫需要授權的服務(Gmail 就是),請改用 可安裝觸發器(時間驅動、表單送出等)。

3.    Exceeded maximum execution time」或跑到一半就停

    原因:Apps Script 單次執行最長 6 分鐘,大量草稿或附件計算很容易超時。

    解法:

        分批處理(例如每次 50 封);

        用 時間驅動觸發器 排程多次;

        透過 PropertiesService 記錄進度(上一個處理到哪個 index)。

4.    寄送或更新失敗,或草稿找不到

    情境:排程執行時找不到特定 draft id,或寄送失敗。

    常見原因:

        草稿被手動刪除/被其他流程改動;

        執行帳號不是草稿建立者;

        觸發器權限或範圍失效(需要重授權)。

    參考:社群案例指出以觸發器執行抓草稿易出現「找不到草稿」錯誤,通常與授權與擁有權相關。

5.    配額踩雷

    郵件收件人/讀寫配額:Gmail 讀寫、寄送都有每日限制。

    郵件大小限制:內文大小(含標頭)與附件數量有限。

        建議先看官方配額頁,並在流程中加入「試算與節流」。

6.    搜尋語法抓不到想要的草稿

        getDrafts() 不吃條件,若你用 GmailApp.search,請確認語法與 Gmail 搜尋盒一致(可用 in:drafts、subject:、newer_than:、before:…)。

7.    內嵌圖片/HTML 模板亂掉

        若用草稿當模板且含內嵌圖片,需正確嵌入 cid: 參照與附件對應。


實務最佳做法

1.    先搜尋後處理:大量草稿時用 GmailApp.search('in:drafts ...') 先縮小集合,再以 isDraft() 二次確認。

2.    小批次+斷點續跑:每輪處理固定數量,超過就把最後索引寫進 PropertiesService,下輪接續。6 分鐘限制下更安全。

3.    尊重審批流程:update() 與 send() 前加「人工確認」旗標(例如試算表欄位 READY=TRUE 才寄)。

4.    格式與安全:HTML 內容以 htmlBody 提供,避免把外部不可信內容直接拼進 HTML(XSS 風險)。

5.    記錄與回溯:寄出後記錄 messageId 與寄送時間,方便之後查核。

6.    權限管理:專案擁有者變更、觸發器「以誰執行」、是否需要重新授權,都要有 SOP。


問題集

Q1:我要只列出「主旨含 XXX」的草稿,getDrafts() 能直接過濾嗎?

不能。請用 GmailApp.search('in:drafts subject:"XXX"') 先找執行緒,再用 isDraft() 篩訊息。

Q2:能在 onEdit(e) 自動寄出草稿嗎?

不建議,因為簡單觸發器不能調用 Gmail 這類需授權服務。改用時間驅動的 可安裝觸發器。

Q3:為什麼同一段程式今天能跑、明天報「需要授權」?

OAuth token 可能失效、觸發器身分改變、或程式碼改動新增了更敏感的 scope。重新授權即可。

Q4:配額到底有哪些需要注意?

包含單次執行 6 分鐘、每日 Gmail 讀寫上限、郵件大小等。規格以官方配額頁為準。


張貼留言 (0)
較新的 較舊