💡 簡單的比喻:全自動飲料店
1. Setting 分頁:就像是店長貼在牆上的SOP 公告(幾點開門、飲料配方代號是什麼)。
2. onFormSubmit (文書員):就像是一個超快手搖飲店員。
◦ 客人一下單(填表單)。
◦ 他馬上拿出貼紙(文件範本)。
◦ 寫上客人的名字(取代
{{姓名}})。 ◦ 封膜(轉成 PDF)。
◦ 直接送到客人手上(寄 Email)。
3. checkFormStatus (警衛):就像是一個定時巡邏的保全。
◦ 每 10 分鐘來店門口看一下。
◦ 如果時間到了晚上 10 點(
END_TIME),他就自動把鐵門拉下來,不讓客人再點餐了。這段程式碼就是幫你省去「手動複製貼上名字做證書」以及「半夜爬起來關閉報名表」的辛苦工作!
💡 這段程式碼就像是聘請了一位**「全能型的活動總召機器人」**。
如果你要在學校辦一場活動,通常需要做兩件很麻煩的事:
第一是有人報名就要馬上發「報名確認函」或「電子證書」給他;
第二是要一直盯著時間,時間到了要把報名表關掉。
這段程式碼就是把這兩件事完全自動化。我們可以把它拆成三個主要部分來理解:
1. 閱讀老闆的指令(讀取設定)
機器人開工的第一件事,是先搞清楚狀況。
• 找筆記本:程式會先去你的 Google 試算表中,尋找一個叫做 "Setting" 的分頁。
• 背下來:這個分頁就像是老闆(你)給機器人的備忘錄,上面寫著重要的資訊,比如:「範本檔案的編號 (ID) 是多少?」、「報名表什麼時候要關?」、「寄信的主旨要寫什麼?」機器人會把這些資料全部讀進腦袋裡記住。
2. 超速文書處理員(自動發證書/通知)
這是程式的核心功能,只要有人送出表單(
onFormSubmit),這位文書處理員就會立刻醒來工作:• 找聯絡人:它會先看報名表,找出填表人的 Email。如果不小心沒填或找不到 Email,它就會停止工作並回報錯誤。
• 準備白紙:它會去拿你指定的「文件範本」(例如:活動證書母片),**影印(複製)**一份新的出來,並幫新檔案取好名字(例如:研習名稱 - 姓名 - 日期),這樣才不會改到原始的母片。
• 填空題:接著,它會打開那份新影印的文件,玩「填空遊戲」。
◦ 它會看報名表裡的資料,比如
{{姓名}}。 ◦ 然後把文件裡對應的
{{姓名}} 全部替換成真的名字(例如:王小明)。 ◦ 不只是文件,連寄出的 Email 主旨和內容,它也會幫你把名字換好,讓信看起來很有誠意。
• 快遞寄送:
◦ 文件填好後,它會自動把它轉成 PDF 檔(就像把證書護貝,比較正式)。
◦ 它還會很貼心地把 Email 內容整理一下(把換行符號變成網頁讀得懂的格式
<br />),確保信件排版漂亮。 ◦ 最後,它會把這份 PDF 當作附件,寫一封信寄給報名的人。
3. 嚴格的警衛(自動開關門)
除了文書處理,這段程式還有另一個功能:
checkFormStatus,這位就像是門口的警衛。• 看時間:這位警衛需要你設定一個鬧鐘(觸發器),例如每 10 分鐘叫他檢查一次。他醒來後會看 "Setting" 裡的設定:現在幾點?活動開始時間 (
START_TIME) 和結束時間 (END_TIME) 是幾點?• 執行門禁:
◦ 如果現在時間在「活動期間內」,而且門是關著的,他就會把表單打開,讓大家報名。
◦ 如果時間已經過了(或是還沒到),而且門開著,他就會把表單關閉,並且掛上一個告示牌(例如:「本表單目前不接受回應」)。
--------------------------------------------------------------------------------
/**
* ------------------------------------------------------------------
* 輔助函式:讀取 "Setting" 工作表中的設定值
* ------------------------------------------------------------------
*/
function getSettings() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Setting');
if (!sheet) {
throw new Error('錯誤:找不到名為 "Setting" 的工作表,請在試算表中建立此分頁。');
}
var lastRow = sheet.getLastRow();
if (lastRow < 2) return {};
var data = sheet.getRange(2, 1, lastRow - 1, 2).getValues();
var settings = {};
data.forEach(function (row) {
var key = row[0];
var value = row[1];
if (key) {
settings[key] = value;
}
});
return settings;
}
/**
* ------------------------------------------------------------------
* 主函式:表單提交時觸發
* ------------------------------------------------------------------
*/
function onFormSubmit(e) {
try {
// 1. 讀取 Setting
var settings = getSettings();
if (!settings.TEMPLATE_ID || !settings.FOLDER_ID) {
Logger.log('錯誤:Setting 工作表中缺少 TEMPLATE_ID 或 FOLDER_ID。');
return;
}
var docTemplateId = settings.TEMPLATE_ID;
var folderId = settings.FOLDER_ID;
var emailSubjectTemplate = settings.EMAIL_SUBJECT || '您的文件已完成';
var emailBodyTemplate = settings.EMAIL_BODY || '您好,您的文件已備妥,請查收附件。';
// 2. 取得表單資料
var formData = e.namedValues;
var recipientEmail = '';
if (formData['Email Address']) recipientEmail = formData['Email Address'][0];
else if (formData['電子郵件地址']) recipientEmail = formData['電子郵件地址'][0];
else if (formData['Email']) recipientEmail = formData['Email'][0];
else if (formData['電子信箱']) recipientEmail = formData['電子信箱'][0];
if (!recipientEmail) {
Logger.log('錯誤:無法在表單回應中找到電子郵件地址,腳本終止。');
return;
}
// 3. 準備檔案與資料夾
var templateFile = DriveApp.getFileById(docTemplateId);
var destinationFolder = DriveApp.getFolderById(folderId);
var schoolName = formData['研習名稱'] ? formData['研習名稱'][0] : '研習活動';
var chineseName = formData['姓名'] ? formData['姓名'][0] : '學員';
var newDocName = schoolName + ' - ' + chineseName + ' - 證明 ' + new Date().toLocaleDateString();
var newDocFile = templateFile.makeCopy(newDocName, destinationFolder);
// 4. 文件內容替換
var newDoc = DocumentApp.openById(newDocFile.getId());
var body = newDoc.getBody();
var finalSubject = emailSubjectTemplate;
var finalBody = emailBodyTemplate;
for (var key in formData) {
if (formData.hasOwnProperty(key)) {
var value = formData[key][0];
// 替換 Word 內容
body.replaceText('{{' + key + '}}', value);
// 替換 Email 變數
var regex = new RegExp('{{' + key + '}}', 'g');
finalSubject = finalSubject.replace(regex, value);
finalBody = finalBody.replace(regex, value);
}
}
newDoc.saveAndClose();
// 5. 轉換 PDF 並寄送
var pdfFile = newDocFile.getAs('application/pdf');
// --- HTML 格式修正區段 ---
// 將試算表的換行符號轉為 HTML <br>
finalBody = finalBody.replace(/\r\n/g, '<br>').replace(/\n/g, '<br>');
GmailApp.sendEmail(recipientEmail, finalSubject, '您的信箱不支援 HTML 顯示', {
htmlBody: finalBody, // 設定 HTML 內容
attachments: [pdfFile.setName(newDocName + '.pdf')],
name: '自動通知系統'
});
// -----------------------
Logger.log('成功:已寄送文件給 ' + recipientEmail);
} catch (error) {
Logger.log('發生嚴重錯誤: ' + error.toString());
}
}
/**
* ------------------------------------------------------------------
* 新增功能:檢查並更新表單的開啟/關閉狀態
* 請設定「時間驅動」觸發器來定期執行此函式 (例如每 10 分鐘)
* ------------------------------------------------------------------
*/
function checkFormStatus() {
try {
// 1. 讀取設定
var settings = getSettings();
var formId = settings.FORM_ID;
// 檢查是否有設定表單 ID
if (!formId) {
Logger.log('錯誤:Setting 工作表中缺少 FORM_ID,無法控制表單開關。');
return;
}
// 取得時間設定 (若未設定則視為 null)
var startTime = settings.START_TIME ? new Date(settings.START_TIME) : null;
var endTime = settings.END_TIME ? new Date(settings.END_TIME) : null;
var closedMessage = settings.CLOSED_MESSAGE || '本表單目前不接受回應。';
// 如果沒有設定時間,就不做任何動作,避免誤關
if (!startTime || !endTime) {
Logger.log('略過:未設定完整的 START_TIME 或 END_TIME。');
return;
}
// 2. 判斷當前狀態
var form = FormApp.openById(formId);
var now = new Date(); // 取得現在時間
var isOpen = false;
// 邏輯:現在時間 必須在 開始與結束 之間
if (now >= startTime && now <= endTime) {
isOpen = true;
}
// 3. 執行開關動作 (只在狀態需要改變時才動作,節省資源)
var currentStatus = form.isAcceptingResponses();
if (isOpen && !currentStatus) {
// 應該開啟,但目前是關閉 -> 執行開啟
form.setAcceptingResponses(true);
Logger.log('表單已自動【開啟】');
}
else if (!isOpen && currentStatus) {
// 應該關閉,但目前是開啟 -> 執行關閉
form.setAcceptingResponses(false);
form.setCustomClosedFormMessage(closedMessage); // 設定關閉訊息
Logger.log('表單已自動【關閉】');
}
else {
Logger.log('檢查完畢:表單狀態無須變更 (目前狀態: ' + (currentStatus ? '開啟' : '關閉') + ')');
}
} catch (error) {
Logger.log('檢查表單狀態時發生錯誤: ' + error.toString());
}
}
2.點選 左側 ⚙️ 專案設定 - ✅在編輯器中顯示「appsscript.json」資訊清單檔案
再回到 < > 編輯器中 將程式貼到 appsscript.json 中即可。
{
"timeZone": "Asia/Taipei",
"dependencies": {},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8",
"oauthScopes": [
"https://www.googleapis.com/auth/script.external_request",
"https://www.googleapis.com/auth/spreadsheets",
"https://www.googleapis.com/auth/presentations",
"https://www.googleapis.com/auth/drive",
"https://www.googleapis.com/auth/script.send_mail",
"https://www.googleapis.com/auth/script.scriptapp",
"https://www.googleapis.com/auth/forms",
"https://www.googleapis.com/auth/documents"
]
}
3.設定 程式 觸發條件 

3.設定 表單 時間 觸發條件 





沒有留言:
張貼留言
歡迎大家一起留言討論!