# 111年 - Google Apps Script雲端程式設計實務班 ###### tags: `Google Apps Script` `產業人才投資方案` `授課資料` > 此項筆記供111年產業人才投資方案-Google Apps Script雲端程式設計實務班課程使用,講師會將上課補充重點、參考資料與程式碼留在此頁面 :books: --- >課程日期:111-12-03(六)、111-12-10 (六) 、111-12-17 (六) 、111-12-24 (六) >課程時間:09:00 ~ 12:00、13:30 ~ 16:30 >上課地點:崑山科技大學 民生應用學院二館 H2605教室 (電競教室) >授課教師:[鄭郁翰](https://web.ksu.edu.tw/DTCECGD/teacher/BPm5DRXmVhiAEs76y8PalQ--) 講師 [[前往教師簡介]](https://web.ksu.edu.tw/DTCECGD/teacher/BPm5DRXmVhiAEs76y8PalQ--) >課程大綱:https://cee.ksu.edu.tw/2517 > >本課程使用之輔助教材:[Google Apps Script雲端自動化與動態網頁實戰(第二版)](http://books.gotop.com.tw/e_ACU084300) >書籍影音教學、範例程式檔下載:http://books.gotop.com.tw/download/ACU084300 ## :memo: 課前準備 ### Step 1: 上課前應準備的 - [x] 完成 Google 帳號申請 - [x] 安裝 Google Chrome 瀏覽器 :::info :pushpin: 因為是使用 Google 的雲端服務,所以使用 Google Chrome 瀏覽器具有最佳體驗 ::: ### Step 2: 課前能力檢核 - [x] Excel使用經驗 - 我曾使用過各項函數完成我的工作 - [x] Excel使用經驗 - 我曾使用過合併列印來產生大量Word檔案 - [x] Excel使用經驗 - 我曾使用過VBA來撰寫巨集程式 - [ ] Google雲端服務使用經驗 - 我曾使用過Google試算表來處理工作 - [ ] Google雲端服務使用經驗 - 我曾使用過Google雲端硬碟分享檔案 - [ ] 我具有程式設計的能力或實際工作經驗 - [ ] 我具有網頁設計的經驗,理解HTML、CSS的架構 - [ ] 我具有網頁設計的經驗,理解JavaSript程式語言 - [ ] 我具有實際使用過 Google Apps Script (GAS) 的經驗 :::info HTML參考資料: https://developer.mozilla.org/zh-TW/docs/Learn/Getting_started_with_the_web/HTML_basics ::: ## 一、Google 試算表 ![](https://hackmd.io/_uploads/rJy3OYyy3.png) - 網路共用的建議:如果希望試算表不公開,只提供特定人士存取,應該採個別授權方式。 參考資料:https://www.isecurity.com.tw/news-and-events/20200611-accidental-exposure-in-google-link-sharing/ - Google試算表提供的函數大多數與Microsoft Excel相同,更完整的函數可參考Google網站的參考文件 - Google試算表常用函數 https://support.google.com/docs/table/25273?hl=zh-Hant ### Google Sheet 的 GAS API 先認識「試算表」的結構,以Microsoft Excel來說,整個檔案就是一個「活頁簿」(workbook),活頁簿下可以有多個「工作表」(worksheet),每個表內又有多個「列」(row)或「欄」(column),以及其下的「儲存格」(cell), ![](https://hackmd.io/_uploads/BkbpuYyy2.png) #### 名稱對照 |項目 | Microsoft Excel | Google Sheet| |------| -------- | -------- | |活頁簿 | Workbook (活頁簿) | Spreadsheet (試算表) | |工作表 | Worksheet (工作頁) | Sheet (工作表) | ### GAS開啟試算表 1. 可以使用openById()或openByUrl() 2. 如果是把GAS附加在試算表上,還可以使用getActiveSpreadsheet()來取得目前「試算表」 3. 取得試算表後,就要決定要開啟哪一個「工作表」,試算表物件下有 getSheetByName('name') 以工作表名稱來取得該工作表物件 * 另外,如果是把GAS附加在試算表上,也可以直接使用getActiveSheet()取得目前試算表中的「第一個工作表」(這樣就可以跳過第3步驟) ```javascript= function myFunction() { var book = SpreadsheetApp.openByUrl('https://docs.google.com/spreadsheets/d/xxxxxxxxxxxxxxx/'); var sheet = book.getSheetByName("工作表1"); var range = sheet.getRange(2,1) Logger.log(range.getValue()); } ``` ```javascript= function myFunction() { var val = SpreadsheetApp.getActiveSheet().getRange(3,1).getValue() Logger.log(val); } ``` ### 如何在試算表使用Google翻譯? 直接使用公式中的函數「==GOOGLETRANSLATE()==」 參考資料:https://support.google.com/docs/answer/3093331?hl=zh-Hant 語系對照表:https://cloud.google.com/translate/docs/languages ```javascript= function onEdit(e) { var c = e.range.getColumn() var r = e.range.getRow() var sheet = SpreadsheetApp.getActiveSheet(); var cn = LanguageApp.translate(e.value,'zh-TW','zh-CN'); var en = LanguageApp.translate(e.value,'zh-TW','en'); var ja = LanguageApp.translate(e.value,'zh-TW','ja'); sheet.getRange(r,c+1).setValue(cn); sheet.getRange(r,c+2).setValue(en); sheet.getRange(r,c+3).setValue(ja); } ``` ![](https://i.imgur.com/V2O4b4L.png) ### Google試算表自訂函數 如果希望在試算表輸入公式時會有自動完成的提示功能,要依格式加入註解 ```javascript= /** * Multiplies the input value by 2. * * @param {number} input The value to multiply. * @return The input multiplied by 2. * @customfunction */ function DOUBLE(input) { return input * 2; } ``` https://developers.google.com/apps-script/guides/sheets/functions?hl=zh-tw#naming 輸入參數也可以輸入一段「範圍」,例如A1:B5 ```javascript= /** * Multiplies the input value by 2. * * @param {number|Array<Array<number>>} input The value or range of cells * to multiply. * @return The input multiplied by 2. * @customfunction */ function DOUBLE(input) { return Array.isArray(input) ? input.map(row => row.map(cell => cell * 2)) : input * 2; } ``` https://developers.google.com/apps-script/guides/sheets/functions?hl=zh-tw#naming 無論如何,自訂函式呼叫必須在 30 秒內傳回;否則,儲存格會顯示錯誤訊息:Internal error executing the custom function. ## 二、Google Apps Script 與網頁應用程式 GAS的網頁應用程式可以接受輸出結果為HTML或JSON格式,寫完程式後存檔,必需使用「部署」功能來發布為網頁應用程式,取得網址後才能使用 ### 如何從GAS顯示資料為HTML? 1. 使用 ContentService.createTextOutput() - 直接輸出文字 ```javascript= function doGet() { return ContentService.createTextOutput("Hello World"); } ``` 2. 使用 HtmlService.createHtmlOutput() - 直接輸出HTML ```javascript= function doGet() { return HtmlService.createHtmlOutput('<b>Hello, world!</b>'); } ``` https://developers.google.com/apps-script/reference/content/content-service 3. 使用 HtmlService.createHtmlOutputFromFile() - 直接使用HTML檔案回傳 ```javascript= function doGet() { return HtmlService.createHtmlOutputFromFile('Index'); } ``` https://developers.google.com/apps-script/guides/html#code.gs 4. 使用 HtmlService.createTemplateFromFile() - 直接使用HTML檔案回傳,並且將其以模板方式設計,可使用<? ... ?> 嵌入GAS程式碼,並且也從GAS端傳入變數,呼叫evaluate()後就會開始執行 Code.js ```javascript= function doGet() { return HtmlService .createTemplateFromFile('Index') .evaluate(); } ``` Index.html ```htmlembedded= <!DOCTYPE html> <html> <head> <base target="_top"> </head> <body> Hello, World! The time is <?= new Date() ?>. </body> </html> ``` https://developers.google.com/apps-script/reference/html/html-template #### 練習一:將變數傳遞到HTML端 code.gs ```javascript= function doGet(e){ var n = 'Tom'; var html = HtmlService.createTemplateFromFile('Index'); html.name = n; return html.evaluate(); } ``` Index.html ```htmlmixed= <!DOCTYPE html> <html> <head> <base target="_top"> </head> <body> <h1> 通訊錄 </h1> <div> 查詢時間:<?= new Date() ?> </div> <div> 維護人員:<?=name ?> </div> </body> </html> ``` ![](https://i.imgur.com/x2BjdGj.png) #### 練習二:結合試算表資料及迴圈呈現結果 code.gs ```javascript= function doGet(e){ var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("編輯人員"); var n = sheet.getRange("A1").getValue(); var sheet2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("工作表1"); var data = sheet2.getRange(2,1, sheet2.getLastRow()-1 ,sheet2.getLastColumn()).getValues(); var html = HtmlService.createTemplateFromFile('Index'); html.name = n; html.data = data; return html.evaluate(); } ``` Index.html ```htmlmixed= <!DOCTYPE html> <html> <head> <base target="_top"> </head> <body> <h1> 通訊錄 </h1> <div> 查詢時間:<?= new Date() ?> </div> <div> 維護人員:<?=name ?> </div> <table border="1">  <tr>   <th>編號</th>   <th>姓名</th> <th>電話</th>   <th>地址</th>  </tr> <? for(var i = 0 ; i< data.length ; i++ ){ ?>  <tr> <td><?=data[i][0] ?></td> <td><?=data[i][1] ?></td> <td><?=data[i][2] ?></td> <td><?=data[i][3] ?></td>  </tr> <? } ?> </table> </body> </html> ``` #### 練習三:分割成不同函式(function)來執行 code.gs ```javascript= function doGet(e){ var html = HtmlService.createTemplateFromFile('Index'); return html.evaluate(); } function getPersonName(){ var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("編輯人員"); return sheet.getRange("A1").getValue(); } function getDataTable(){ var sheet2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("工作表1"); var data = sheet2.getRange(2,1, sheet2.getLastRow()-1 ,sheet2.getLastColumn()).getValues(); return data; } ``` Index.html ```htmlmixed= <!DOCTYPE html> <html> <head> <base target="_top"> </head> <body> <h1> 通訊錄 </h1> <div> 查詢時間:<?= new Date() ?> </div> <div> 維護人員:<?=getPersonName() ?> </div> <table border="1">  <tr>   <th>編號</th>   <th>姓名</th> <th>電話</th>   <th>地址</th>  </tr> <? var data = getDataTable(); for(var i = 0 ; i< data.length ; i++ ){ ?>  <tr> <td><?=data[i][0] ?></td> <td><?=data[i][1] ?></td> <td><?=data[i][2] ?></td> <td><?=data[i][3] ?></td>  </tr> <? } ?> </table> </body> </html> ``` ### 設計具有表單互動的GAS的網頁應用程式 :::info GAS參考資料:https://developers.google.com/apps-script/guides/web HTML表單介紹 : https://www.fooish.com/html/form.html (網路資料) ::: 程式中主要其中包含 ==doGet(e)== 或 ==doPost(e)== 函式,並回傳HTML內容,可以利用函式中的傳入參數 ==e== 來取得使用者的輸入並與其互動。 傳入參數(e)中常見的成員屬性: - e.queryString - e.parameter - e.parameters - e.pathInfo - e.postData :::danger :exclamation: 請特別注意:在 Google Apps Script 中,參數(parameter)的名稱請不要使用 ==c== 及 ==sid== ,這兩個名稱為系統所保留作為其他用途,請不要在程式中使用這些參數名稱。 ::: 如果網頁想要求使用者輸入資料,並由後端接收並處理,可以使用HTML建立表單(Form)後傳到後端處理,但因為GAS的網頁應用程式實際上是執行在iframe框架內,所以設計上稍有差異,請依據以下網址進行設計:https://developers.google.com/apps-script/guides/html/communication#forms HTML表單(Form)的重要屬性 - method:可以設定成GET或POST,分別由GAS的 doGet(e) 及 doPost(e) 處理 - name:表單名稱 - action:表單資料送往的目的地 - onsubmit : 指定表單提交前要執行的JavaScript function 有關 action ,如果要送給目前程式處理,可以簡單使用 ==ScriptApp.getService().getUrl()== 取得目前頁面網址。如果希望在表單中嵌入,可以參考以下方式: ```htmlmixed== <form method="POST" action="<?= ScriptApp.getService().getUrl() ?>"> 姓名:<input type="text" name="name" id="name" ></input> <input type="submit" value="送出" /> </form> ``` 在Google官方文件中,更推薦使用 ==google.script.run== 來達到非同步的函式呼叫(如同AJAX的方式使用),使用 ==google.script.run== 可以從前端JavaScript程式碼執行指定的後端(.gs)函式。 ### 資料查詢的程式範例 https://docs.google.com/spreadsheets/d/1aHY7BZlcI4_jh8KcEl-9R6wXljVbrtqpnKziLZfEwuE/edit?usp=sharing ![](https://i.imgur.com/YyiGnby.png) ![](https://i.imgur.com/TwF1yf1.png) #### 其他常見方法: - HtmlOutput.setTitle(title) : 設定網頁標題 - HtmlTemplate.evaluate() : 評估回傳的HtmlOutput物件 - 網頁客戶端也可以使用JavaScript呼叫一些專屬功能,名稱皆以「google.script」開頭,例如「google.script.host.close()」是把目前對話方塊關閉。 -- google.script.history 用於GAS的網頁應用程式, -- google.script.host 用於Google服務的側邊欄或對話方塊 -- google.script.run 用於非同步執行GAS伺服器端方法時 -- google.script.url 可以查詢網址來取得目前的網址參數和片段,只能使用在網頁應用程式 - 更多基本API參考 : https://developers.google.com/apps-script/reference/html ## 三、Gmail 電子郵件的應用 在Google Apps Script內建的服務呼叫函式中,分別有 ==Mail Service== 及 ==Gmail Service== 最為常見,其中 Mail Service 這項服務可讓指令碼代表使用者傳送電子郵件。 ==若與 Gmail Service 比較,Mail Service 只用來做「傳送」電子郵件==,無法存取使用者的 Gmail 帳戶;所以,如果需要涉及 Gmail 更完整的控制,例如讀取信件清單這類型的複雜工作,請改採用 Gmail Service 來操作。 :::info :pushpin: 有關電子郵件發送的詳細使用方式及函式清單,可以參考以下網址: Mail Service : https://developers.google.com/apps-script/reference/mail Gmail Service : https://developers.google.com/apps-script/reference/gmail?authuser=1 ::: ### 起手式:簡單的電子郵件發送功能 1. 使用 Mail App 發送電子郵件 ```javascript= var subject = "信件標題"; var message = "您好,這裡是信件內容"; var email = "xxx@gmail.com" MailApp.sendEmail (email, subject, message); ``` 2. 另外一種呼叫方式: ```javascript= MailApp.sendEmail( { to: "xxx@gmail.com", subject: "信件標題", htmlBody: "信件內文<hr />也可以包含HTML語法喔!<br />" } ); ``` 3. 此外,也可以使用 Gmail App 來發送電子郵件 ```javascript= var subject = "信件標題"; var message = "您好,這裡是信件內容"; var email = "xxx@gmail.com" GmailApp.sendEmail (email, subject, message); ``` :::info :pushpin: 使用 Google Apps Script 發送郵件具有配額限制,如果想知道剩餘多少發送數量,可以呼叫 ==getRemainingDailyQuota()== 這個函式來查詢剩餘額度。 * 詳細配額限制資訊 : https://developers.google.com/apps-script/guides/services/quotas ::: ### 發送具有附件的電子郵件 1. 發送電子郵件也可以夾帶附件,可以參考以下兩個官方範例教學 - MailApp : https://developers.google.com/apps-script/reference/mail/mail-app#sendEmail(String,String,String,Object) - GmailApp : https://developers.google.com/apps-script/reference/gmail/gmail-app?authuser=1#sendEmail(String,String,String,Object) ```javascript= var blob = DriveApp.getFileById("換成你的ID"); var subject = "信件標題(MailApp)"; var message = "您好,這裡是信件內容"; var email = "xxx@xxx.xxx.edu.tw" MailApp.sendEmail(email, subject, message, { attachments: [blob] }); ``` :::info :pushpin: 請注意:電子郵件夾帶附件也具有相關限制(例如檔案大小),使用時也要留意 ::: 2. 如果檔案放在外部,也可以使用 ==UrlFetchApp== 下載回來,但是 UrlFetchApp 也有配額限制,且下載過程可能需要等待,不建議大量呼叫。 ```javascript= var f = UrlFetchApp.fetch("https://xx.xx/x.jpg").getBlob().setName('檔案名稱'); ``` UrlFetchApp呼叫fetch()後,幾個常見的函數: - getContentText() 取得文字資料 - getBlob() 取得二進制大型物件 - getContent() 取得原始二進制資料 - getResponseCode() 取得HTTP Status Code ```javascript= var file = UrlFetchApp.fetch("https://cee.ksu.edu.tw/CourseImage.ashx?id=2517&type=2").getBlob().setName('課程海報'); var subject = "信件標題(MailApp)"; var message = "您好,這裡是信件內容"; var email = "xxxx@xxx.tw" MailApp.sendEmail(email, subject, message, { attachments: [file] }); ``` ## 四、Google 表單 :::info :pushpin: 參考資料 : https://developers.google.com/apps-script/reference/forms ::: 可以使用 ==JSON.stringify()== 函式將物件轉換為JSON格式的字串,以利於開發與偵錯。 ```javascript= Logger.log( JSON.stringify(e) ); ``` 可以使用 https://jsonformatter.curiousconcept.com/ 快速格式化JSON內容 Google表單「表單提交時」條件觸發時所傳入參數的格式 ![](https://i.imgur.com/fmT8XxG.png) - 當表單提交時,自動發信的程式碼範例 ```javascript= function SendMail(e) { var subject = "【活動報名成功通知】" + e.values[2] + "您好,已成功報名活動!" ; var message = e.values[2] + "您好,已於" + e.values[0] + "成功報名活動!"; var email = e.values[3]; MailApp.sendEmail (email, subject, message); } ``` - 建立電子郵件的「收信確認」功能 ```javascript= function doGet(e){ var id = e.parameter.id; var email = e.parameter.email; var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("表單回應 1"); var data = sheet.getRange(2,1,sheet.getLastRow()-1,sheet.getLastColumn()).getValues(); Logger.log(email); Logger.log(id); var atIndex = data.findIndex(function(item, index, array){ return item[3] === email && item[6] === id; }); if(atIndex<0){ return ContentService.createTextOutput( "驗證失敗"); } else{ atIndex += 2 ; if(!sheet.getRange(atIndex,8).getValue()){ return ContentService.createTextOutput( "已完成驗證過,請勿重複執行!"); } var now = new Date(); sheet.getRange(atIndex,8).setValue(now); return ContentService.createTextOutput( "完成信件開啟之驗證"); } } function SendMail(e) { var random = Math.random().toString(36); var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("表單回應 1"); sheet.getRange(e.range.rowStart ,7).setValue(random); var email = e.values[3]; var subject = "【活動報名成功通知】" + e.values[2] + "您好,已成功報名活動!" ; var message = e.values[2] + "您好,已於" + e.values[0] + "成功報名活動!請開啟以下網址完成收信確認:" + "https://script.google.com/xxxxxxxxxxxx/exec?email=" + email + "&id=" + random ; MailApp.sendEmail (email, subject, message); } ``` Forms Service 可讓指令碼建立、存取及修改 Google 表單 ### 開啟現存Google表單 開啟一個現有的 Google 表單,可以發現其實跟Google試算表、Google雲端硬碟等服務都一樣,幾乎都是使用 ==openById()== 即可開啟。也可以使用 ==openByUrl()== 喔! ```javascript= var myForm = FormApp.openById('xxxxIdxxxx'); ``` ### 建立一個新的Google表單 ```javascript= var myForm = FormApp.create('表單名稱'); ``` :::info :pushpin: 無論是開啟現有或建立一個新的表單,最終都會回傳一個Form類別的物件回來,相關資料可參考: https://developers.google.com/apps-script/reference/forms/form ::: ### 使用GAS程式碼依據試算表內容自訂建立Google表單(線上測驗) https://docs.google.com/spreadsheets/d/1_oTXQM9okAc-7UF3TmBt1cFf3njcu4ZECdnkFflvpzQ/edit?usp=sharing ### GAS 中 FormApp 常用的表單元件與函式對照 新增表單輸入項目: - addTextItem() 簡答(提供填寫單行文字) - addParagraphTextItem() 詳答(提供填寫多行文字) - addMultipleChoiceItem() 選擇題(圓形勾選鈕) - addCheckboxItem() 核取方塊(方形勾選鈕) - addListItem() 下拉式選單 - addScaleItem() 線性刻度 - addGridItem() 單選方格 - addCheckboxGridItem() 核取方塊格 - addDateItem() 日期(提供日期輸入) - addDurationItem() 時間(僅供時間輸入) - addDateTimeItem() 日期與時間 新增表單非輸入項目: - addImageItem() 新增圖片配置 - addVideoItem() 影片項目 ## 五、Google Workspace 自訂選單 :::info :pushpin: 參考資料 : https://developers.google.com/apps-script/guides/menus ::: 可以透過指令碼擴充 Google Workspace 應用程式(例如文件、試算表)的功能選單。若希望在使用者開啟檔案時就在工具列上顯示自訂選單,可在 ==onOpen()== 函式中撰寫擴充自訂選單的程式碼。 ### 整合自訂選單的線上測驗產生器 https://docs.google.com/spreadsheets/d/10ZKHNKXpPVxm31bxH3ds8yDIYOs4ah9JAOo4CJ5puto/edit?usp=sharing ## 六、Google 雲端硬碟 :::info :pushpin: 有關透過Google Apps Script操作雲端硬碟之詳細使用方式及函式清單,可以參考以下網址: Drive Sevice : https://developers.google.com/apps-script/reference/drive ::: ### 範例程式 : 建立檔案 ```javascript= var fileName = "xxx.txt"; var content = "1234"; DriveApp.createFile(fileName,content); ``` 小提示:把createFile()換成createFolder(name),就是建立資料夾的方法 ### 在指定資料夾建立檔案的方法 ```javascript= function myFunction() { var fileName = "xxx.txt"; var content = "1234"; var folder = DriveApp.getFolderById("輸入你的資料夾ID"); folder.createFile(fileName,content); } ``` ### DriveApp 常用函數 - createFile() 建立檔案 - createFolder() 建立資料夾 - getFileById() 取得指定ID的檔案 - getFilesByName() 取得指定名稱的檔案(可能取得多個檔案) - getFilesByType() 取得指定MIME Type的檔案(可能取得多個檔案) - getFiles() 取得所有檔案的集合 - getFolderById() 取得指定ID資料夾 - getFolders() 取得所有資料夾的集合 - getFoldersByName(name) 取得指定名稱的資料夾(可能取得多個資料夾) - getRootFolder() 取得根目錄(最上層)的資料夾 ### 取得檔案基本資料 ```javascript= function getFileInfo() { var file = DriveApp.getFileById("你的檔案ID"); Logger.log(file.getName()) Logger.log(file.getSize()) Logger.log(file.getDescription()) Logger.log(file.getUrl()) Logger.log(file.getDownloadUrl()) Logger.log(file.getId()) Logger.log(file.getMimeType()) Logger.log(file.getLastUpdated()) } ``` ### Class File 常用函數 - makeCopy 複製檔案 - moveTo 移動檔案 - getId 取得檔案ID - getUrl 取得檔案URL(可用於在Google應用程式中開啟 File 的網址) - getDownloadUrl 取得檔案下載網址 * - getBlob 以blob形式取得檔案 - getName 取得檔案名稱 - getMimeType 取得檔案MIME Type - setTrashed 設定檔案是否在垃圾桶 - setContent 設定檔案內容(或覆蓋) - setDescription 設定檔案說明內容 資料夾的操作方式類似,請參見Class Folder的介紹: https://developers.google.com/apps-script/reference/drive/folder ### 將雲端硬碟檔案清單寫入試算表 ```javascript= function getFileInfo() { var sheet = SpreadsheetApp.getActiveSheet(); sheet.clear(); var headerRow = ['檔案名稱','檔案大小','檔案描述','檔案網址', '實際下載網址']; sheet.appendRow(headerRow) var folder = DriveApp.getFolderById("資料夾ID"); var files = folder.getFiles(); while (files.hasNext()) { var file = files.next(); var data = []; data.push(file.getName()) data.push(file.getSize()) data.push(file.getDescription()) data.push(file.getUrl()) data.push(file.getDownloadUrl()) sheet.appendRow(data); } } ``` ### 完整程式碼範例(參考書籍實作) GAS ```javascrip= function onOpen() { var sheet = SpreadsheetApp.getActive(); var menuItems = [ {name: '載入清單', functionName: 'getFileInfo'} ]; sheet.addMenu('雲端硬碟', menuItems); } function getFileInfo() { var sheet = SpreadsheetApp.getActiveSheet(); sheet.clear(); var headerRow = ['檔案名稱','檔案大小','檔案描述','檔案網址', '實際下載網址']; sheet.appendRow(headerRow) var folder = DriveApp.getFolderById("資料夾ID"); var files = folder.getFiles(); while (files.hasNext()) { var file = files.next(); //file.setSharing(DriveApp.Access.ANYONE,DriveApp.Permission.VIEW); var data = []; data.push(file.getName()) data.push(file.getSize()) data.push(file.getDescription()) data.push(file.getUrl()) data.push(file.getDownloadUrl()) sheet.appendRow(data); } } function getData(){ var sheet = SpreadsheetApp.getActiveSheet(); var data = sheet.getDataRange().getValues(); return data; } function doGet(e) { return HtmlService.createTemplateFromFile('index').evaluate().setTitle('檔案下載'); } ``` HTML: ```htmlmixed= <!DOCTYPE html> <html> <head> <base target="_top"> </head> <body> <h1>檔案下載</h1> <? var data = getData(); for (var i = 1; i< data.length; ++i) { ?> 檔案名稱:<?= data[i][0] ?> <a href="<?= data[i][4] ?>">[下載]</a> <br /> <? } ?> </body> </html> ``` HTML(書上版本): ```htmlmixed= <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initialscale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>檔案下載</title> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> <base target="_top"> <style> body{ font-family: 'Microsoft JhengHei'; } .download-list{ padding: 0; list-style: none; } .download-list li{ padding: 15px 5px; border-bottom: 1px solid #999; } .download-list li:nth-last-child(1){ border-bottom: 0; } .download-list li a{ margin-left: 10px; padding: 5px 15px; background: #333; color: #fff; border-radius: 20px; text-decoration: none; } .download-list li a:hover{ background: #666; } </style> </head> <body> <div class="container"> <div class="row"> <div class="col-12"> <h1 class="text-primary text-center mt-5 mb-3">檔案下載</h1> <ul class="download-list"> <? var data = getData(); for (var i = 1; i< data.length; ++i) { ?> <li> <span><?= data[i][0] ?></span> <a href="<?= data[i][4] ?>"> 下載</a> <div> <?= data[i][2] ?> </div> </li> <? } ?> </ul> </div> </div> </div> </body> </html> ``` ### 改為直接讀取雲端硬碟 ```javascript= function getData(){ var folder = DriveApp.getFolderById("資料夾ID"); var files = folder.getFiles(); var table = []; while (files.hasNext()) { var file = files.next(); var data = []; data.push(file.getName()) data.push(file.getSize()) data.push(file.getDescription()) data.push(file.getUrl()) data.push(file.getDownloadUrl()) table.push(data); } Logger.log(table); return table; } ``` ## 七、Google 文件 :::info :pushpin: 有關透過Google Apps Script操作文件之詳細使用方式及函式清單,可以參考以下網址: Document Sevice : https://developers.google.com/apps-script/reference/document ::: ### 開啟及建立文件 開啟檔案的方法 (可用ID或URL開啟) ```javascript= var doc1 = DocumentApp.openById('文件的ID'); var doc2 = DocumentApp.openByUrl('文件的URL'); ``` 建立檔案的方法 ```javascript= doc = DocumentApp.create('檔案名稱'); ``` ### Google 文件的結構 參考資料:https://developers.google.com/apps-script/guides/docs Document→Body→.... 對於內容的操作,大多從getBody()開始 ### Class Document 常用函數 - getBody 取得文件中的內文 - getAs 取回指定類型的blob內容,僅支援轉換為PDF,請傳入「application/pdf」參數 - ### Class Body 常用函數 #### 範例程式:新增段落 ```javascript= var doc = DocumentApp.getActiveDocument(); var body = doc.getBody(); body.appendParagraph("A paragraph."); body.appendPageBreak(); //參考來源: https://developers.google.com/apps-script/reference/document/body ``` #### 範例程式:文字內容取代 基本的文字替換 ```javascript= var body = DocumentApp.getActiveDocument().getBody(); body.replaceText("{{name}}", "Tom"); ``` 帶有運算規則的作法 ```javascript= var body = DocumentApp.getActiveDocument().getBody(); body.replaceText("^.*Apps ?Script.*$", "Apps Script"); ``` 備註:這不是JavaScript的正規表達式,而是Google的RE2規則,若要進一步認識可參考 https://github.com/google/re2 / https://github.com/google/re2/wiki/Syntax / https://support.google.com/a/answer/1371417?hl=zh-Hant #### 範例程式、文字取代及PDF產生 :::info :pushpin: 請先使用 Google文件 來設計文件範本格式,並將要替換的部分使用 =={{...}}== 來標註(也可以用其他符號標註,重點是要使用程式碼來取代對應的標籤部分),也可以使用 Microsoft Word 設計完後在上傳到 Google雲端硬碟 ,然後將其轉換為 Google文件 的格式。 ::: ```javascript= function createDocumentForTemplater() { var templater_id = "範本檔案的ID"; var templater_file = DriveApp.getFileById(templater_id).makeCopy(); var doc = DocumentApp.openById(templater_file.getId()); var body = doc.getBody(); body.replaceText("{{name}}" , "王曉明"); body.replaceText("{{id}}", "12345678"); doc.saveAndClose(); var folder_id = "目標存放資料夾的ID"; var folder = DriveApp.getFolderById(folder_id); templater_file.moveTo(folder).setName("202212170001"); var pdf = templater_file.getAs('application/pdf'); folder.createFile(pdf).setName("202212170001.pdf"); } ``` #### 再改善程式碼:產生PDF後寄信 ```javascript= function createDocumentForTemplater() { var sheet = SpreadsheetApp.getActiveSheet(); var data = sheet.getDataRange().getValues(); for(var i = 1; i < data.length; i++ ){ var templater_id = "範本ID"; var templater_file = DriveApp.getFileById(templater_id).makeCopy(); var doc = DocumentApp.openById(templater_file.getId()); var body = doc.getBody(); var name = data[i][0]; var person = data[i][1]; var date = data[i][2]; var email = data[i][3]; body.replaceText("{{name}}" , name ); body.replaceText("{{日期}}" , date ); body.replaceText("{{頒獎人}}" , person ); doc.saveAndClose(); var folder_id = "存放資料夾ID"; var folder = DriveApp.getFolderById(folder_id); templater_file.moveTo(folder).setName(name + "證書"); var pdf = templater_file.getAs('application/pdf'); folder.createFile(pdf).setName(name + "證書.pdf"); var subject = "恭喜你成績及格,請下載電子證書"; var message = name + "您好,這裡是信件內容"; MailApp.sendEmail(email, subject, message, { attachments: [pdf] }); } ``` ### 擴充選單:靈活的文件合併功能 ```javascript= function onOpen(e) { var ui = SpreadsheetApp.getUi(); ui.createMenu('合併檔案') .addItem('開始合併', 'start') .addToUi(); } function start(){ var ui = SpreadsheetApp.getUi(); var tid; var fid; var filename; var response1 = ui.prompt( '文件範本', '請設定文件範本的ID', ui.ButtonSet.OK_CANCEL); if(response1.getSelectedButton() == ui.Button.OK){ tid = response1.getResponseText(); } else { return; } var response2 = ui.prompt( '產生文件存放區', '請設定文件產生後要儲存的資料夾的ID', ui.ButtonSet.OK_CANCEL); if(response2.getSelectedButton() == ui.Button.OK){ fid = response2.getResponseText(); } else { return; } var response3 = ui.prompt( '檔案名稱', '請設定所產生的檔案名稱', ui.ButtonSet.OK_CANCEL); if(response3.getSelectedButton() == ui.Button.OK){ filename = response3.getResponseText(); } else { return; } createDocumentForTemplater(tid,fid,filename); } function createDocumentForTemplater( tid , fid , filename) { var sheet = SpreadsheetApp.getActiveSheet(); var data = sheet.getDataRange().getValues(); for(var i = 1; i < data.length; i++ ){ var templater_id = tid; var templater_file = DriveApp.getFileById(templater_id).makeCopy(); var doc = DocumentApp.openById(templater_file.getId()); var body = doc.getBody(); for(var x = 0 ; x < data[i].length ; x++ ){ var h = data[0][x]; var v = data[i][x]; body.replaceText( "{{" + h + "}}" , v ); } doc.saveAndClose(); var folder_id = fid; var folder = DriveApp.getFolderById(folder_id); templater_file.moveTo(folder).setName(filename + "_" + i); } } ``` ### 送出表單測驗後,判斷及格就寄發證書 :::info :pushpin: 這個證書範本是使用 Microsoft Word 2019 中的範本產生,然後上傳到 Google雲端硬碟並將其轉換為 Google文件 的格式。 ::: ```javascript= function sendReport(e) { var templater_id = "範本ID"; var score = parseInt(e.values[1].split("/")[0]); var name = e.values[3]; var email = e.values[14]; if(score >= 60){ var templater_file = DriveApp.getFileById(templater_id).makeCopy(); var doc = DocumentApp.openById(templater_file.getId()); var body = doc.getBody(); body.replaceText("{{name}}" , name ); doc.saveAndClose(); var folder_id = "存放資料ID"; var folder = DriveApp.getFolderById(folder_id); templater_file.moveTo(folder).setName(name + "-證書"); var pdf = templater_file.getAs('application/pdf'); folder.createFile(pdf).setName(name + "-證書.pdf"); var subject = "恭喜你成績及格,請下載電子證書"; var message = name + "您好,這裡是信件內容"; MailApp.sendEmail(email, subject, message, { attachments: [pdf] }); } else{ var subject = "考試成績不及格"; var message = name + "您好....."; MailApp.sendEmail(email, subject, message,); } } ``` **及格通知信** ![](https://i.imgur.com/rcZYOQ2.png) **證書檔案成果** ![](https://i.imgur.com/cGbiAP1.png) **不及格通知信** ![](https://i.imgur.com/UY7HUVq.png) ## 八、LINE Notify 整合 :::info :pushpin: 如果需要 LINE Notify 更完整的功能整合,請參考 LINE Notify API 官方文件 : https://notify-bot.line.me/doc/ ::: LINE Notify是LINE所提供的一項服務,請先開啟以下 LINE Notify 網站網址,並加入 LINE Notify 的好友 https://notify-bot.line.me/zh_TW/ LINE Notify的 LINE ID: @linenotify ![](https://i.imgur.com/8voeeyw.png) LINE Notify 可以個別一對一傳送訊息給目標,也可以將訊息傳送到特定群組上 1. 將 LINE Notify 帳號加入好友清單後,請開啟 https://notify-bot.line.me/zh_TW/ 網站並從右上角的「登入」按鈕開啟登入頁面,並登入您的帳號 2. 登入後,從右上角的選單開啟「個人頁面」 ![](https://i.imgur.com/PvMpoPY.png) 3. 進入後,在==「發行存取權杖(開發人員用)」==點選==「發行權杖」== ![](https://i.imgur.com/gol1rVT.png) 4. 選擇「群組」或「1對1模式」 ![](https://i.imgur.com/YU7FB8a.png) 5.取得 LINE Notify 權杖,請務必==先複製==再關閉喔! ![](https://i.imgur.com/kAuZj8U.png) 6.發行結果如下,如要取消可點選「解除」 ![](https://i.imgur.com/8iIhKYb.png) 權杖發行或解除後,也會透過LINE訊息通知喔! ![](https://i.imgur.com/oRcRjYY.png) ### LINE Notify 的限制 - 單一個人申請的發行權杖==每小時最多能發送1000則訊息== - 每則訊息不可超過==1000個字== - 每個基本使用者最多可以申請==100個權杖== - 圖片大小也有限制,更詳細的限制可以參考API文件 ![](https://i.imgur.com/5zoqZuF.png) ### 最基本的 LINE Notify 訊息發送程式範例 ```javascript= function sendLineNotifyMessage() { var token = '請在這裡貼入 LINE Notify 發行權杖'; var options = { "method" : "POST", "headers" : {"Authorization" : "Bearer "+ token}, "payload":{ 'message': 'Hello World!' } }; UrlFetchApp.fetch("https://notify-api.line.me/api/notify", options); } ``` :::success :pushpin: UrlFetchApp可以用來擷取、連結外部服務或資源,讓 Google Apps Script 程式碼可以與其他應用程式通訊(發出HTTP請求),詳細參考文件 : https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app ::: **程式碼執行後的通知訊息發送結果:** 請注意,發送的訊息前方會加上 ==【...】== 來標示建立時所設定的權杖名稱, ![](https://i.imgur.com/8Da8l1S.png) ### 透過 LINE Notify 訊息發送圖片 LINE Notify 也可以發送圖片,可採用以下2種方式,將參數添加到原本的JSON結構上傳遞: 1. 加上imageFullsize (完整圖片,最大尺寸為 2048×2048px JPEG) 及imageThumbnail (縮圖, 最大尺寸為 240×240px JPEG),分別傳入URL即可。 2. 加上imageFile,傳入照片檔案的二進制格式,支援JPEG及PNG格式;這個動作會將圖片上傳到LINE伺服器上。(具有每小時上傳次數的限制) :::danger 備註: 如果imageFullsize、imageThumbnail、imageFile三個屬性都一起傳送,則會LINE Notify優先使用imageFile。 > 附帶一提,LINE Notify也可以傳遞貼圖,只要照API文件內的說明傳入stickerPackageId及stickerId參數即可。 ::: #### 將雲端硬碟上的圖片上傳至 LINE Notify ```javascript= function sendLineNotifyMessage() { var token = '你的權杖'; var imageFile = DriveApp.getFileById("你雲端硬碟檔案上的ID").getBlob(); var options = { "method" : "POST", "headers" : {"Authorization" : "Bearer "+ token}, "payload":{ 'message': 'GAS訊息測試_只傳圖片' , 'imageFile' : imageFile, } }; UrlFetchApp.fetch("https://notify-api.line.me/api/notify", options); } ``` #### LINE Notify 傳遞貼圖 ```javascript= function sendLineNotifyMessage() { var token = '你的權杖'; var options = { "method" : "POST", "headers" : {"Authorization" : "Bearer "+ token}, "payload":{ 'message': 'GAS訊息測試_只傳圖片' , 'stickerPackageId' : '789' , 'stickerId' : '10855', } }; UrlFetchApp.fetch("https://notify-api.line.me/api/notify", options); } ``` #### 行程通知範例 :::success :pushpin: 底下用於Date日期/時間格式化的函數format取自網路上的範例,來源如下網址: https://blog.scottchayaa.com/post/2019/05/27/javascript_date_memo/ ::: ```javascript= var token = '你的權杖'; Date.prototype.format = function (fmt) { var o = { "M+": this.getMonth() + 1, //月份 "d+": this.getDate(), //日 "h+": this.getHours(), //小時 "m+": this.getMinutes(), //分 "s+": this.getSeconds(), //秒 "q+": Math.floor((this.getMonth() + 3) / 3), //季度 "S": this.getMilliseconds() //毫秒 }; if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length)); for (var k in o) if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); return fmt; } function main() { var data = SpreadsheetApp.getActiveSheet().getDataRange().getValues(); var now = new Date(); for(var i = 1; i < data.length; i++ ){ if(now.toDateString() == data[i][2].toDateString() ){ var msg = "\n---今日行程通知---\n"; msg += "活動名稱:" + data[i][0] + "\n"; msg += "活動地點:" + data[i][1]+ "\n"; msg += "活動日期:" + data[i][2].format("yyyy-MM-dd"); sendLineNotifyMessage(msg) } } } function sendLineNotifyMessage(str) { var options = { "method" : "POST", "headers" : {"Authorization" : "Bearer "+ token}, "payload":{ 'message': str } }; UrlFetchApp.fetch("https://notify-api.line.me/api/notify", options); } ``` ### 開放資料 (Open Data) :::info :pushpin: 常見的政府部門 Open Data 平臺 1. 氣象資料開放平臺 https://opendata.cwb.gov.tw/ 2. 政府資料開放平臺 https://data.gov.tw/ 3. 經濟部水利署水利資料開放平台 https://opendata.wra.gov.tw/ ::: #### 台南市36小時天氣範例程式碼 ```javascript= var token = '你的權杖'; var url = '天氣的URL'; Date.prototype.format = function (fmt) { var o = { "M+": this.getMonth() + 1, //月份 "d+": this.getDate(), //日 "h+": this.getHours(), //小時 "m+": this.getMinutes(), //分 "s+": this.getSeconds(), //秒 "q+": Math.floor((this.getMonth() + 3) / 3), //季度 "S": this.getMilliseconds() //毫秒 }; if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length)); for (var k in o) if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); return fmt; } function main() { var response = UrlFetchApp.fetch(url); var data = JSON.parse(response); var locationData = data["cwbopendata"]["dataset"]["location"]; var tainanData = locationData.find(function(item, index, array){ return item["locationName"] == "臺南市" }); var data_wx = tainanData["weatherElement"].find(function(item, index, array){ return item["elementName"] == "Wx" }); var data_pop = tainanData["weatherElement"].find(function(item, index, array){ return item["elementName"] == "PoP" }); var data_ci = tainanData["weatherElement"].find(function(item, index, array){ return item["elementName"] == "CI" }); var msg = "臺南市天氣36小時預報\n\n"; for(var i = 0 ; i < 3 ; i++ ){ var start = new Date(data_wx["time"][i]["startTime"]) var end = new Date(data_wx["time"][i]["endTime"]) var wx = data_wx["time"][i]["parameter"]["parameterName"] var pop = data_pop["time"][i]["parameter"]["parameterName"] var ci = data_ci["time"][i]["parameter"]["parameterName"] msg += start.format("yyyy-MM-dd hh:mm") + "~" + end.format("yyyy-MM-dd hh:mm") msg += "\n天氣概況:" + wx msg += "\n降雨機率:" + pop + "%" msg += "\n舒適程度:" + ci msg += "\n------------\n" } sendLineNotifyMessage(msg); } function sendLineNotifyMessage(str) { var options = { "method" : "POST", "headers" : {"Authorization" : "Bearer "+ token}, "payload":{ 'message': str } }; UrlFetchApp.fetch("https://notify-api.line.me/api/notify", options); } ``` ![](https://i.imgur.com/FNRP6xB.png) #### 解析XML格式 - 以中央通訊社政治新聞為例 ```javascript= var token = '你的權杖'; var url = 'https://feeds.feedburner.com/rsscna/politics'; function main() { var response = UrlFetchApp.fetch(url); var data = XmlService.parse(response); var root = data.getRootElement(); var channel = root.getChild("channel"); var items = channel.getChildren("item"); var msg = "中央通訊社政治新聞 \n\n"; for(var i = 0 ; i < items.length ; i++ ){ msg += items[i].getChild("title").getText(); msg += "\n"; msg += items[i].getChild("link").getText(); msg += "\n------------\n" } sendLineNotifyMessage(msg); } function sendLineNotifyMessage(str) { var options = { "method" : "POST", "headers" : {"Authorization" : "Bearer "+ token}, "payload":{ 'message': str } }; UrlFetchApp.fetch("https://notify-api.line.me/api/notify", options); } ``` ![](https://i.imgur.com/0WeFl47.png) ### LINE Notify 登錄服務 前面已經學到如何發送 LINE Nofity 訊息的基本技能了,但卻只能發送給開發者自己的帳號或指定的聊天室。 如果需要發送給不特定人,就需要完整的整合登入機制,首先要先將你的服務登錄到 LINE Notify 後台,並依據其所提供的 OAuth 機制設計一個網頁介面提供登入認證,登入後取得權杖後即可發送訊息給特定LINE帳號。 先體驗一下:https://script.google.com/a/macros/g.ksu.edu.tw/s/AKfycbyad4UTDy94iORdObOSqsRXgkFz6CBj5ehfEx5j3lq4QnLsON-pW5YJl6uKG69NtdNqPQ/exec #### 1. 基本流程概觀: 1. 呼叫 https://notify-bot.line.me/oauth/authorize ,取得使用者的授權碼。如果使用者沒有登入LINE,則會跳到LINE的登入介面。 2. 呼叫 https://notify-bot.line.me/oauth/token ,取得對應的權杖(Token),此權杖就是日後要發送訊息給這位使用者的依據,所以程式後端應該將這個權杖儲存起來,以供日後訊息發送之用。 3. 接下來,就可以使用上述取得的權杖進行訊息發送。 #### 2. 連結功能的具體開發流程: 步驟一、到 LINE Nofity 網頁完成服務登錄並取得相關資訊 請前往 : https://notify-bot.line.me/my/services/ ![](https://i.imgur.com/PsbJXYS.png) 步驟三、建立提供使用者登錄的網頁介面 重要小提示:重要且不對外開放呼叫的函式應該設為私有函式,也就是要在函式名稱後加上下底線(_) ```htmlmixed= <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <base target="_top"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous"> <script> function linkLineNotify() { var URL = 'https://notify-bot.line.me/oauth/authorize?'; URL += 'response_type=code'; URL += '&client_id=<?=client_id ?>'; URL += '&redirect_uri=<?=redirect_uri ?>'; URL += '&scope=notify&state=NO_STATE'; window.open(URL, "帳號驗證", "top"); } </script> <title>LINE Notify 測試</title> </head> <body> <div class="container"> <div class="row"> <div class="col-12"> <div class="d-grid"> <button onclick="linkLineNotify();" class="btn btn-primary"> 連結 LINE Notify 通知服務 </button> </div> </div> </div> </div> </body> </html> ``` 步驟三、接收Callback處理函式(用GET接收) ```javascript= var redirect_uri = "URL"; var client_id = "你的client_id"; var client_secret = "你的client_secret"; function doGet(e){ if(e.parameter.code){ var data = JSON.parse(getAccessToken_(e.parameter.code)); var str = "連結成功!你的權杖是: " + data.access_token; SpreadsheetApp.getActiveSheet().appendRow([data.access_token]); return HtmlService.createHtmlOutput(str); } else{ return HtmlService.createTemplateFromFile("index").evaluate().addMetaTag('viewport', 'width=device-width, initial-scale=1'); } } function sendMessageToAll(){ var data = SpreadsheetApp.getActiveSheet().getDataRange().getValues(); for(var i = 0; i < data.length ; i++ ){ sendLineNotifyMessage_("Hello" , data[i][0]) } } function getAccessToken_(code){ var options = { "method" : "post", "Content-Type" : "application/x-www-form-urlencoded", "payload" : { "grant_type" : "authorization_code", "code" : code, "redirect_uri": redirect_uri, "client_id" : client_id, "client_secret" : client_secret } }; var response = UrlFetchApp.fetch("https://notify-bot.line.me/oauth/token", options); return response; } function sendLineNotifyMessage_(str,token) { var options = { "method" : "post", "headers" : {"Authorization" : "Bearer "+ token}, "payload":{ 'message': str , 'notificationDisabled' : true, } }; var response = UrlFetchApp.fetch("https://notify-api.line.me/api/notify", options); } ``` 小提示:加上 ==.addMetaTag('viewport', 'width=device-width, initial-scale=1')== 是因為 Google Apps Script的網頁被放置在iframe中,為了達到RWD的效果,必須在外層網頁加上這段Meta Tag。 網頁顯示結果: ![](https://i.imgur.com/6I653Cf.png) 點選連結按鈕並登入後: ![](https://i.imgur.com/r4HPNwL.png) ![](https://i.imgur.com/uYP0wXC.png) ![](https://i.imgur.com/Q1GDRYI.png) --- # 課外補充:HTML常用元素 Web入門: HTML 基礎 https://developer.mozilla.org/zh-TW/docs/Learn/Getting_started_with_the_web/HTML_basics ### 圖片 ```HTML== <img src="images/firefox-icon.png" alt="My test image" /> ``` ### 標題 ```HTML== <h1>My main title</h1> <h2>My top level heading</h2> <h3>My subheading</h3> <h4>My sub-subheading</h4> ``` ### 段落 ```htmlembedded== <p>This is a single paragraph</p> ``` ### 清單 ```htmlembedded= <ul> <li>technologists</li> <li>thinkers</li> <li>builders</li> </ul> ``` 備註: ol=有序清單 ul=無序清單 ### 表格 ```htmlembedded= <table border="1">  <tr>   <th>這裡是第一行的第一個欄位</th>   <th>這裡是第一行的第二個欄位</th>  </tr>  <tr>   <td>這裡是第二行的第一個欄位</td>   <td>這裡是第二行的第二個欄位</td>  </tr> </table> <table border="1">  <tr>   <td>這裡是第一行的第一個欄位</td>   <td>這裡是第一行的第二個欄位</td>  </tr>  <tr>   <td>這裡是第二行的第一個欄位</td>   <td>這裡是第二行的第二個欄位</td>  </tr> </table> <table border="1"> <thead> <tr> <th>這裡是第一行的第一個欄位</th> <th>這裡是第一行的第二個欄位</th> </tr> </thead> <tbody> <tr> <td>這裡是第二行的第一個欄位</td> <td>這裡是第二行的第二個欄位</td> </tr> </tbody> </table> ``` ### 超連結 ```htmlembedded== <a href="https://cee.ksu.edu.tw">崑山科技大學推廣教育課程網</a> <br /> <a href="https://www.ksu.edu.tw">崑山科技大學首頁</a> ``` > 備註:上面的 br 標籤代表換行 --- # 其他參考資料 ## 課程使用網址 1. Google Apps Script : https://script.google.com/ 2. Google Drive (雲端硬碟) : https://drive.google.com/ 3. Google Docs (文件) : https://docs.google.com/ 4. LINE Notify : https://notify-bot.line.me/zh_TW/ 5. YAMM (Yet Another Mail Merge) : https://yamm.com/ 6. JSFiddle (輔助你製作網頁及看到結果) : https://jsfiddle.net/ 7. HTML.cafe (另一個輔助製作網頁的工具) : https://html.cafe/ ### 參考資料 :bookmark_tabs: 1. 課程大綱:https://cee.ksu.edu.tw/2517 2. 輔助教材書籍網址:http://books.gotop.com.tw/v_ACU084300 3. Google官方參考資料 - GAS指南 : https://developers.google.com/apps-script/overview?authuser=1 (可以從這邊了解到一些 Google Apps Script 基本介紹、開發架構等資料,如果你想把VBA轉移到GAS,這邊也會有資料) 4. Google官方參考資料 - 參考資料 : https://developers.google.com/apps-script/reference?authuser=1 (當你想知道特定服務如何呼叫,例如想利用GAS存取試算表、想利用GAS建立文件、想利用GAS建立行事曆...,來這邊找就對了!) 5. Google官方參考資料 - 其他範例 : https://developers.google.com/apps-script/samples?authuser=1 6. 服務斷線記錄 : https://developers.google.com/apps-script/guides/support/outage-log?authuser=1 7. W3Schools - HTML Tutorial : https://www.w3schools.com/html/ 8. Google官方參考資料 - 將GAS發布為外掛程式 : https://developers.google.com/apps-script/add-ons/how-tos/publish-add-on-overview 9. html表單教學: https://www.fooish.com/html/form.html 10. Google Apps Script 免費電子書(PDF)資源 : https://riptutorial.com/ebook/google-apps-script 11. https://blog.miniasp.com/post/2020/02/17/Go-Through-LINE-Notify-Without-Any-Code ### JavaScript Array(陣列)處理方法 常見的方法:filter(), find(), forEach(), map(), every(), some(), reduce() 參考資料: 1. https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Array 2. https://www.casper.tw/javascript/2017/06/29/es6-native-array/ ### JavaScript 資料型態強制轉型 參考資料: - https://hackmd.io/@s_jpXuNwRQiUuGCOQAOZuA/BkJlweeIF - https://www.cythilya.tw/2018/10/15/coercion/ 字串轉數值 - parseInt() - parseFloat() - Number() 轉換為字串 - String() - toString() - 各項變數可呼叫的函式 ### 利用 .new 網域名稱來快速開啟Google服務 :::info :pushpin: 參考報導與部落格介紹: - https://www.ithome.com.tw/news/139155 - https://free.com.tw/google-docs-new-domain-trick/ ::: Google 透過 .new 網域名稱整合了許多常見的服務,透過簡短網址就可以直達各式服務入口,目前已有上百個 .new 網址提供使用,詳細內容請參考 Google 的網站介紹與說明 : https://whats.new/ - doc.new 建立文件 - sheet.new 建立試算表 - slide.new 建立簡報 - form.new 建立表單 - script.new 建立Google Apps Script - site.new 建立協作平台(Goolge Site) - ### 參考實例 1. 使用 Gmail 合併郵件和 Google 試算表 https://developers.google.com/apps-script/samples/automations/mail-merge 2. 自訂函式:計算行車距離 https://developers.google.com/apps-script/samples/custom-functions/calculate-driving-distance 3. 自訂函式:計算分級折扣 https://developers.google.com/apps-script/samples/custom-functions/tier-pricing ### 其他網路參考資源 1. https://github.com/googleworkspace/apps-script-samples 2. https://spreadsheet.dev/google-apps-script-tutorial / https://spreadsheet.dev/ 3. https://docs.google.com/spreadsheets/d/1Lk6OClOPA8p94fspQrs8-M-W080tb244U-fWGqvnApk/edit#gid=1018260646 4. https://www.wfublog.com/search/label/%E9%9B%BB%E8%85%A6-%20Google-Apps%20Script?&max-results=5 5. https://www.labnol.org/code/google-quiz-score-200301 6. 將多欄位整合為不同形式呈現(20221217範例) https://docs.google.com/spreadsheets/d/1b0I5qN_afALt5c3YjtY32Sy_iXF4vc-X2yPJPdo13zY/edit?usp=sharing 7. cheerio for Google Apps Script https://github.com/tani/cheeriogs 8. https://tanaikech.github.io/tags/google-apps-script/ 9. LINE BOT 管理自動化實務分享 - 臺灣學術網路 https://noc.tanet.edu.tw/index.php/download/seminar/seminar-1080121?download=558:1080121-03 10. apps-script-oauth2 https://github.com/googleworkspace/apps-script-oauth2 11. https://percy10442.pixnet.net/blog/post/479711174-%E7%B5%90%E5%90%88googleg%E8%A9%A6%E7%AE%97%E8%A1%A8%E8%88%87line-notify%EF%BC%8C%E5%AE%9A%E6%99%82%E9%80%9A%E7%9F%A5%E8%B3%87%E7%94%A2%E7%8B%80 12. https://blog.scottchayaa.com/post/2019/05/27/javascript_date_memo/ ### 如何在 Google Apps Script 使用 Google BigQuery ? 參考資料 1. https://developers.google.com/apps-script/advanced/bigquery 2. https://greenido.wordpress.com/2013/12/16/big-query-and-google-spreadsheet-intergration/ ---