Google Apps script

by dumpling

  • 水餃 aka 10
  • 北資一六學術
  • 正在努力學英文
  • 簡報第一次用這個風格感覺很酷
  • 前幾天可能只有在音愛劇出現

whoami

建立專案

Spreadsheet

Form

Gmail

Calendar

Web

Web

Web

觸發條件

參考資料

Google Apps script

Javascript

{建立專案}

App Script 首頁建立

前往 script.google.com

點左上角「+ 新專案」

# 建立專案

從雲端硬碟建立

開啟雲端硬碟,依序點選「新增」→「更多」→「Google Apps Script」

# 建立專案

建立成功畫面

# 建立專案

{JavaScript 基礎語法}

Logger.log()

#Logger.log()

變數宣告與賦值

let a;
a = 123;
Logger.log(a); // 123

const b = 456; //常數
Logger.log(b); // 456

#Javascript

常見資料型態

let number = 42;              // 數字
let string = "文字";           // 字串
let boolean = true;           // 布林值
let date = new Date();        // 日期
let array = [1, 2, 3];        // 陣列
let object = { name: "John" };// 物件

Logger.log(typeof number);    // number

#Javascript

變數命名規範

  • 必須以 英文字母、_ 或 $ 開頭(不可用數字)
  • 不能包含空格
  • 可包含數字(0–9)
  • 英文大小寫有差異(例如:Name ≠ name
  • 不可使用 JavaScript 保留字

#Javascript

算數運算子

+:加法

-:減法

*:乘法

/:除法

%:取餘數

**:次方

let a = 10;
let b = 3;
Logger.log(a + b);  // 13
Logger.log(a - b);  // 7
Logger.log(a * b);  // 30
Logger.log(a / b);  // 3.33333333333333
Logger.log(a % b);  // 1
Logger.log(a ** b); // 1000

#Javascript

賦值運算子

=:基本賦值

+=:加法並賦值

-=:減法並賦值

*=:乘法並賦值

/=:除法並賦值

%=:取餘並賦值

let x = 10;

x += 5; // 等價於 x = x + 5,結果為 15
x -= 3; // 等價於 x = x - 3,結果為 12
x *= 2; // 等價於 x = x * 2,結果為 24
x /= 4; // 等價於 x = x / 4,結果為 6
x %= 5; // 等價於 x = x % 5,結果為 1

#Javascript

條件判斷(if / else)

let score = 48763;

if (score >= 60) {
  Logger.log("及格");
} else if (score >= 50){
  Logger.log("去補考");
} else {
  Logger.log("你被死當");
}

#Javascript

比較運算子

>: 大於

< : 小於

>= : 大於等於

<= : 小於等於

== : 值相等

!= : 值不相等

=== : 全等(值與型態都相等)

!== : 不全等

let y = 5;
Logger.log(y == "5");  // true
Logger.log(y === "5"); // false
Logger.log(y != 3);    // true
Logger.log(y !== "5"); // true
Logger.log(y > 3);     // true
Logger.log(y <= 5);    // true

#Javascript

邏輯運算子

&&:邏輯AND

||:邏輯OR

!:邏輯NOT

// && (AND):兩邊都為 true,結果才為 true
Logger.log(true && false);  // false
Logger.log(true && true);   // true

// || (OR):只要一邊為 true,結果就是 true
Logger.log(true || false);  // true
Logger.log(false || false); // false

// ! (NOT):反轉布林值
Logger.log(!true);  		 // false
Logger.log(!false);  		 // true

#Javascript

字串(String)

  • 單引號 ' '
  • 雙引號 " "
  • 反引號 ` `
    • 模板字串要使用反引號
Logger.log("hello")
Logger.log('hello')
Logger.log(`hello`)

let age = 16;
Logger.log(`dumpling is ${age} years old`); // 用反引號

#Javascript

字串操作

let name1 = "   dumpling";
let name2 = "ling  "
let name = name1 + " " + name2;
Logger.log(name); // "   dumpling ling  "

name = name.trim();
Logger.log(name);// "dumpling ling"
Logger.log(name.charAt(2));   // m
Logger.log(name.indexOf("p"));// 3
Logger.log(name.length);      // 13
Logger.log(name.toUpperCase());// DUMPLING LING
Logger.log(name.replace("dump", "ling")); // lingling ling
Logger.log(name.includes("dum")) // true
Logger.log(name.substring(9, 13)); // ling
Logger.log(name.split(" "));    // [dumpling, ling]

#Javascript

函式(Function)

// 宣告一個名為 add 的函式
// x、y是外部傳入的參數
function add1(x, y) { 
    return x + y; // 回傳結果給呼叫函式的地方
}

Logger.log(add1(3, 5)); // 8

#Javascript

其他寫法

// 箭頭函式 	
const add2 = (a, b) => {
  return a + b;
};
    
// 只有回傳值可以簡寫成這樣
const add3 = (a, b) => a + b;

Logger.log(add2(3, 5)); // 8
Logger.log(add3(3, 5)); // 8

#Javascript

物件(Object)

  • 物件由多個屬性(property) 組成

  • 每個屬性是鍵值對(key : value)

  • value 可以是:

    • 數字

    • 字串

    • 布林值

    • 函式

    • 物件

let object = { key: value, key2: value2 };

#Javascript

let person = {
    name: "dumpling", // 字串
    age: 16, // 數字
    graduated: false, // 布林值
    greet: function(){ // 函式
        Logger.log("hi ckefgisc")
    },
    school: { // 物件
        highschool: "TFG",
        university: "NTU"
    }
};

Logger.log(person.name); // dumpling
Logger.log(person.school.highschool); // TFG
person.greet(); // hi ckefgisc

#Javascript

陣列

用來存放有序的資料集合

let scores = [80, 90, 100];

Logger.log(scores);     // [80, 90, 100]
Logger.log(scores[0]); // 80
Logger.log(scores.length); // 陣列長度 = 3

#Javascript

陣列操作

scores[1] = 70; // 80, 70 ,100
scores.sort((a, b) => a - b); // 70, 80, 100
scores.reverse(); // 100, 80, 70
scores.push(85); // 100, 80, 70, 85
scores.pop(); // 100, 80, 70
scores.unshift("gooddd"); // gooddd, 100, 80, 70
scores.shift();// 100, 80, 70
Logger.log(scores.indexOf(70)); // 2

#Javascript

多維陣列

let arr = [
  ['00', '01', '02'],
  ['10', '11', '12'],
  ['20', '21', '22']
];
Logger.log(arr[1][2]); // 12

#Javascript

for 迴圈

let fgisc = ["dumpling", "claire", "children"];

for (let i = 0; i < fgisc.length; i++) {
    Logger.log(fgisc[i]); // dumpling, claire, children
}
// i = 0:從第 0 個元素開始
// i < scores.length:跑到陣列最後一個元素
// i++:每次加 1

for (let key in fgisc){
    Logger.log(key); // 0, 1, 2
}
for (let item of fgisc){ 
    Logger.log(item); // dumpling, claire, children
}
fgisc.forEach((item) => {
    Logger.log(item); // dumpling, claire, children
});

#Javascript

while 迴圈

let i = 0;
while (i < fgisc.length) {
    Logger.log(fgisc[i]);
    i++;
}

do {
  Logger.log(fgisc[0]);
  i++;
} while (i < fgisc.length);

#Javascript

取得年、月、日、星期、小時

Logger.log(now.getFullYear()); // 2025
Logger.log(now.getMonth() + 1); // 12
Logger.log(now.getDate()); // 27
Logger.log(now.getDay());  // 6
Logger.log(now.getHours());  // 21
Logger.log(now.toLocaleDateString());  // 12/27/2025
let now = new Date();
Logger.log(now); //Sat Dec 27 21:43:34 GMT+08:00 2025

#Javascript

日期(Date)

{Spreadsheet}

直接取得試算表

  • .gs 檔有連結試算表
  • 試算表已經開啟
  • .getActiveSpreadsheet() 取得目前的試算表
  • .getActiveSheet() 取得目前的工作表
// 取得目前的試算表 
let spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
 // 獲取工作表
let sheet = spreadsheet.getActiveSheet();

#Spreadsheet

透過試算表ID開啟

  • .openById('試算表ID') 取得試算表
  • .getSheets() 取得工作表
// 開啟試算表
let spreadsheet = SpreadsheetApp.openById('試算表ID');
let sheets = spreadsheet.getSheets(); // 所有工作表(陣列)
let sheet = sheets[0];  // 第一個工作表

#Spreadsheet

建立新試算表

  • .create('試算表名稱') 建立新試算表並命名
  • .insertSheet('newSheet') 建立新工作表
  • .getId() 獲取試算表 id
  • .getUrl() 獲取試算表 URL
let newSpreadsheet = SpreadsheetApp.create('試算表名稱');
let newsheet = newSpreadsheet.insertSheet('newSheet'); 
let spreadsheetId = newSpreadsheet.getId();
let spreadsheetUrl = newSpreadsheet.getUrl();

#Spreadsheet

讀取儲存格

  • 使用陣列儲存工作表中的資料
  • 讀取單一儲存格
    • .getRange("方塊名稱").getValue() 使用方塊名稱
    • .getRange(行, 列).getValue() 使用儲存格座標
let spreadsheet = SpreadsheetApp.openById('試算表ID');
let sheets = spreadsheet.getSheets(); // 獲取所有工作表
// sheets[0] 表示第一個工作表

let value = sheets[0].getRange("A1").getValue();
let value = sheets[0].getRange(1, 1).getValue(); 

#Spreadsheet

讀取儲存格

  • 讀取多個儲存格
    • .getRange("方塊名稱:方塊名稱").getValues() 使用方塊名稱
    • .getRange(起始列, 起始欄, 列數, 欄數).getValues() 使用儲存格座標
let values = sheets[0].getRange("A1:B5").getValues();
let values = sheets[0].getRange(1, 1, 5, 2).getValues();

#Spreadsheet

寫入儲存格

寫入單一儲存格 .setValue()

sheets[0].getRange("A1").setValue("value");

#Spreadsheet

寫入儲存格

寫入多個儲存格 .setValues()

 // 開啟試算表
  let spreadsheet = SpreadsheetApp.openById('ID');
  // 獲取所有工作表
  let sheets = spreadsheet.getSheets(); 

  let data = [
    ["姓名", "年齡"],
    ["水餃", 16],
    ["麻麻", 17]
  ];
  sheets[0].getRange(1, 1, 3, 2).setValues(data);
  // 起始列, 起始欄, 列數(data.length), 欄數(data[0].length)

#Spreadsheet

{Gmail}

MailApp vs GmailApp

傳送郵件使用 .sendEmail()

GmailApp.sendEmail(
  "recipient@example.com",
  "郵件主旨",
  "郵件內容"
);
MailApp.sendEmail(
  "recipient@example.com",
  "郵件主旨",
  "郵件內容"
);

#Gmail

MailApp vs GmailApp

MailApp GmailApp
顯示在寄件備份
傳送附件
操作收件匣讀取、搜尋、刪除信件
需要的權限 較低 較高

#Gmail

使用 GmailApp 寄純文字郵件

  • GmailApp.sendEmail(收件者, 主旨, 內文);
function sendTextMail() {
  GmailApp.sendEmail(
    "test@example.com", // 收件者
    "測試信件",          // 主旨
    "這是 GAS 寄出的純文字信件" // 內文
  );
}
  • 收件者可為單一 Email 或多個(以逗號分隔)
  • 內文為純文字(不支援 HTML 樣式)

#Gmail

HTML 格式郵件

可設定粗體、顏色、表格

function sendHtmlMail() {
  GmailApp.sendEmail(
    "iloveckefgisc@gmail.com", // 收件者
    "煮紙", // 主旨
    "如果你看到這行代表不支援 HTML", //當收件者不支援 HTML 時顯示
    {
      htmlBody: `
        <h2 style="color:blue">成績單</h2>
        <ul>
          <li>數學:100</li>
          <li>物理:100</li>
          <li>我也想考這樣</li>
        </ul>
      `
    }
  );
}

#Gmail

搜尋郵件

GmailApp.search

  • from: 來自指定寄件人
  • to: 寄給指定收件人
  • subject: 主旨包含文字
  • has: 內文包含文字
let threads = GmailApp.search('from:example@example.com');
GmailApp.search('to:example@example.com');
GmailApp.search('subject:remind');
GmailApp.search('has:word');

#Gmail

獲取郵件內容

  • getSubject() 取得主旨
  • getFrom() 取得寄件者
  • getTo() 取得收件者
  • getDate() 寄送時間
  • getPlainBody() 純文字內文
  • getBody() HTML 內文

#Gmail

獲取郵件內容

function readSubjects() {
  // 搜尋來自指定寄件人的郵件(Thread)
  let threads = GmailApp.search('from:example@example.com');

  threads.forEach((thread) => {
    // 取得該對話串中的所有信件
    let messages = thread.getMessages();

    messages.forEach((message) => {
      let subject = message.getSubject();
      Logger.log(`主題: ${subject}`);
    });
  });
}

#Gmail

{Form}

建立新表單並取得 ID

  • FormApp.create():建立全新的 Google 表單
  • getId():之後可用來重新開啟此表單
// 建立新表單,並回傳表單 ID
function createNewForm(formName) {
  let newForm = FormApp.create(formName); // 建立新表單
  let formId = newForm.getId();          // 取得表單 ID
  return formId;                         // 回傳字串
}

#Form

以 ID 開啟表單

let form = FormApp.openById(formId);

#Gmail

建立表單並取得連結

  • getEditUrl():給管理者編輯
  • getPublishedUrl():給使用者填寫
function createFormWithUrls(formName) {
  let newForm = FormApp.create(formName);

  let editUrl = newForm.getEditUrl();       // 編輯用連結
  let publishedUrl = newForm.getPublishedUrl(); // 填寫用連結

  let urls = {
    edit: editUrl,
    published: publishedUrl
  };

  return urls;
}

#Gmail

取得目前作用中的表單

  • 只適用於綁定在 Google 表單的 GAS
  • 獨立專案會回傳 null
let form = FormApp.getActiveForm();

#Gmail

新增題目(以選擇題為例)

  • addMultipleChoiceItem():新增選擇題
  • setTitle():題目文字
  • createChoice():建立選項
  • showOtherOption(true):允許填寫其他答案
function addQuestion(formId) {
  let form = FormApp.openById(formId);

  let item = form.addMultipleChoiceItem();
  item.setTitle("你最喜歡的動物?");

  item.setChoices([
    item.createChoice("貓"),
    item.createChoice("狗")
  ]);

  item.showOtherOption(true); // 顯示「其他」選項
}

#Gmail

讀取表單回應

function getFormResponses() {
  let form = FormApp.getActiveForm();
  let responses = form.getResponses(); // 取得所有填寫紀錄

  responses.forEach((response) => {
    let answers = response.getItemResponses();

    answers.forEach((answer) => {
      Logger.log("問題:" + answer.getItem().getTitle());
      Logger.log("回應:" + answer.getResponse());
    });
  });
}

#Gmail

{Calendar}

操作行事曆

  • 不需要知道行事曆 ID
  • 操作的就是登入帳號的主要行事曆
CalendarApp.getDefaultCalendar();

#Calender

建立行事曆事件

.createEvent()

  • Date(年, 月, 日, 時, 分)
  • 月份從 0 開始
function createCalendarEvent() {
  let calendar = CalendarApp.getDefaultCalendar(); // 取得預設行事曆

  calendar.createEvent(
    "小社課", // 事件標題
    new Date(2025, 11, 18, 12, 10),  // 開始時間(2025/12/18 12:10)
    new Date(2025, 11, 18, 12, 40), // 結束時間(2025/12/18 12:40)
    {
      location: "電腦教室",      // 地點
      description: "水餃的最後一堂中午小社"   // 說明
    }
  );
}

#Calender

#Calender

查詢指定日期的事件

  • .getEvents(startTime, endTime)
  • .getTitle() 標題
  • .getStartTime() 時間
function getEventsOnDate() {
  let calendar = CalendarApp.getDefaultCalendar();

  let startTime = new Date(2025, 11, 18);  // 2025/12/18 00:00
  let endTime = new Date(2025, 11, 19);   // 2025/12/19 00:00

  let events = calendar.getEvents(startTime, endTime);

  events.forEach(function(event) {
    Logger.log(
      "標題: " + event.getTitle() +
      ", 時間: " + event.getStartTime()
    );
  });
}

#Calender

#Calender

刪除事件

先用時間範圍找到事件,確認事件標題,刪除

function delEvent() {
  let calendar = CalendarApp.getDefaultCalendar();

  let startTime = new Date(2025, 1, 5, 10, 0);
  let endTime = new Date(2025, 1, 5, 11, 0);

  let events = calendar.getEvents(startTime, endTime);

  events.forEach(function(event) {
    if (event.getTitle() === "會議") {
      event.deleteEvent();
      Logger.log("已刪除事件:" + event.getTitle());
    }
  });
}

#Calender

#Calender

{Web}

  • 左上角 + 新增 html 檔案

#Web

  • 寫好 html
    • script 要寫在 head
<!DOCTYPE html>
<html>
  <head>
    <script>
      function submitForm() {
        let name = document.getElementById("name").value;
        let club = document.getElementById("club").value;
        let grade = document.getElementById("grade").value;

        google.script.run.withSuccessHandler(() => alert("提交成功!")).saveData(name, club, grade);
      }
    </script>
  </head>
  <body>
    <h1>資料填寫</h1>
    <label>姓名:</label> <input type="text" id="name"><br>
    <label>社團:</label> <input type="text" id="club"><br>
    <label>年級:</label> <input type="text" id="grade"><br>
    <button onclick="submitForm()">提交</button>
  </body>
</html>

#Web

  • 寫好 .gs
function doGet() {
  return HtmlService.createHtmlOutputFromFile("html檔案名稱");
}

function saveData(name, club, grade) {
  let sheet = SpreadsheetApp.openById("試算表ID").getSheetByName("工作表名稱");
  sheet.appendRow([name, club, grade, new Date()]);
}

#Web

  • 點選「部署」→「新增部署作業」
  • 類型選擇「網頁應用程式」
  • 設定存取權限

  • 取得網址

#Web

{觸發條件}

觸發條件

  • 點選左上角觸發條件

#觸發條件

觸發條件

  • 新增觸發條件

#觸發條件

{成發}

{參考資料}

2026建北電資聯合寒訓 GAS簡報

By yiling

2026建北電資聯合寒訓 GAS簡報

20260208 我是不是快退休了

  • 53