iT邦幫忙

2025 iThome 鐵人賽

DAY 3
0
自我挑戰組

一路side project - 學習筆記系列 第 3

[Day 3] GAS 觀念:語言、執行環境、權限、常用內建服務與 HTTP 呼叫實戰

  • 分享至 

  • xImage
  •  

今天延伸昨天 [Day 2] 群組不可缺的夥伴 - Line Bot Reminder 的主題,來看 GAS 內部吧

1) GAS 到底是什麼?

  • Google Apps Script(GAS)= 雲端執行的 JavaScript(V8 引擎)
    我們將寫好的程式碼放在 Google 的執行環境,呼叫一堆「內建服務」(如 UrlFetchApp、ScriptApp、PropertiesService、ContentService…)來做事。
  • 不是 Node.js
    語法相容 ES2019+(let/const、箭頭函式、模板字串、Map/Set、async/await 可用),但不能 npm install,也沒有 Node 的核心模組(fs、net、crypto 之類)。
  • 程式入口
    任何頂層函式都能被執行或綁定成觸發器(e.g. sendDailyReminder())。要接 HTTP,則實作 doGet(e)/doPost(e),再用 Web App 形式對外。

2) 先備觀念:身分、權限、配額

使用別人的服務最好先了解一些配置,這邊分成三個要注意的地方:

  • 執行身分
    程式以「腳本擁有者」或「部署者」的身分去叫 Google API/對外發 HTTP。第一跑常會跳授權流程(OAuth scopes)。
  • OAuth Scopes(權限聲明)
    取決於你用到的服務(例如 UrlFetchApp、SpreadsheetApp)。首次使用會要求你允許。
  • 配額(Quotas)
    例如:單次執行時間上限、每日呼叫次數、UrlFetchApp 的外呼限制等。設計排程與重試機制時要考慮(像是本米前面Line Bot所用的是免費方案,屬於輕量使用,每月可以200則訊息)。

3) 內建服務(Services)

「服務」可以想像成 GAS 提供的 library ,以下是 LINE Bot 常用的:

  • UrlFetchApp:對外 HTTP(打 LINE Messaging API 靠它)
  • ContentService:組 HTTP 回應(回 LINE Webhook 一定要 200 OK)
  • PropertiesService:存環境設定(token、groupId…),三種層級:Script / User / Document(最常用 Script)
  • ScriptApp:排程觸發器(定時叫某個函式)
  • Utilities:雜項工具(HMAC、Base64、時間格式化、Zip…)
  • CacheService / LockService:快取與鎖(避免同時寫入或限速)
  • Logger / console.log:記錄執行 Log,事後在「執行紀錄」看
  • (延伸)進階 Google 服務:SpreadsheetApp、DriveApp、GmailApp…;或「Advanced Services」對應正式的 Google APIs(需在編輯器啟用)

像在這裡的 Reminder 就會用到 UrlFetchApp、ContentService、PropertiesService、ScriptApp...等


4)實際例子

以昨天的 Line Bot Reminder 當案例:
首先,昨天也有說到,對外傳要先有 金鑰 (也就是昨天存的 LINE_CHANNEL_ACCESS_TOKEN) ,
這是 LINE 機器人的頻道存取憑證 (Channel Access Token)。
所以在一開始,我們可以先將變數/參數等設好,下面接我們所要用到的function。

1. 設定區

const PROPS = PropertiesService.getScriptProperties();
function readToken_() {
  const t = PROPS.getProperty('LINE_CHANNEL_ACCESS_TOKEN');
  if (!t) throw new Error('缺少 LINE_CHANNEL_ACCESS_TOKEN(Script Properties)');
  return t;
}
const REMINDER_TEXT = '🔔 每日最後提醒:請在今天上傳/填寫妳的 iThome 喔~';

2. Webhook:用來擷取 Group ID (doPost(e))

function doPost(e) {
  const out = ContentService.createTextOutput('OK').setMimeType(ContentService.MimeType.TEXT);
  try {
    const raw = (e && e.postData && e.postData.contents) ? e.postData.contents : '';
    if (!raw) return out; // Verify/健康檢查 → 直接 200
    const body = JSON.parse(raw);
    const events = body.events || [];
    const has = PROPS.getProperty('GROUP_ID');
    for (const ev of events) {
      if (!has && ev.source && ev.source.type === 'group' && ev.source.groupId) {
        PROPS.setProperty('GROUP_ID', ev.source.groupId); // 只存一次
        console.log('Saved GROUP_ID=' + ev.source.groupId);
      }
    }
  } catch (err) { console.error('doPost error:', err); }
  return out;
}
  • 接收 LINE 平台傳來的事件,解析後找出群組 ID (groupId),並使用 PROPS.setProperty('GROUP_ID', ...) 將其儲存一次到指令碼屬性中。
  • 當在設定了 Webhook 的群組中進行操作時(如發送訊息、有人加入/離開),LINE 就會將一個包含事件資訊的 JSON 資料包以 HTTP POST 方式發送到 Web App 網址。

3. 健康檢查 (doGet(e))

function doGet() {       // 健康檢查
  return ContentService.createTextOutput('ok');
}
  • 功能:處理所有對 Web App 的 HTTP GET 請求,主要做健康檢查與手動測試
  • 補充用途:
    1. 部署驗證:當將專案部署為 Web App 後,可以直接在瀏覽器中開啟該網址。
      如果頁面顯示 "ok",代表部署成功且服務正在運行。
    2. LINE Webhook 驗證:在 LINE Developers 後台點擊 Verify,平台會送 POST 到 Webhook 並檢查 200 OK。

4. 發送群組提醒 (sendDailyReminder())

function sendDailyReminder() {
  const groupId = PROPS.getProperty('GROUP_ID');
  if (!groupId) {
    console.log('GROUP_ID 未設定,請先完成一次性 Webhook 擷取流程。');
    return;
  }
  const url = 'https://api.line.me/v2/bot/message/push';
  const payload = {
    to: groupId,
    messages: [{ type: 'text', text: REMINDER_TEXT }]
  };
  const options = {
    method: 'post',
    headers: { 'Authorization': 'Bearer ' + readToken_() }, 
    contentType: 'application/json',
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
  };
  const res = UrlFetchApp.fetch(url, options);
  console.log('Push result: ' + res.getResponseCode() + ' - ' + res.getContentText());
}
  • 實際執行推播訊息的函式,由每日定時器呼叫。
  • API 說明:此函式呼叫的是 LINE 的 "Push Message" API (/v2/bot/message/push)。
    Push API 允許機器人主動向任何曾與之互動過的用戶或群組發送訊息。

5. 建立每日定時器 (setupDailyTrigger())

function setupDailyTrigger() {
  ScriptApp.getProjectTriggers()
    .filter(t => t.getHandlerFunction() === 'sendDailyReminder')
    .forEach(t => ScriptApp.deleteTrigger(t));

  ScriptApp.newTrigger('sendDailyReminder')
    .timeBased()
    .atHour(20)     // 20:00
    .everyDays(1)
    .create();

  console.log('Daily trigger created for 20:00 Asia/Taipei.');
}
  • 功能:只需在 GAS 編輯器中手動執行一次,即可建立一個每日定時器。
    https://ithelp.ithome.com.tw/upload/images/20250916/201547648oXDdofUD6.png

6. 小工具:查看 Group ID (showGroupId())

function showGroupId() {
  console.log('GROUP_ID = ' + (PROPS.getProperty('GROUP_ID') || '(尚未擷取)'));
}
  • 簡單的偵錯 (Debug) 工具。
  • 用途:當完成了 Webhook 設定,但不確定 Group ID 是否成功擷取時,不必去翻找 PropertiesService 的設定介面。直接在 GAS 編輯器中手動執行此函式,就能在下方的「執行紀錄」中看到目前儲存的 Group ID,或得知 "(尚未擷取)",非常方便。
    https://ithelp.ithome.com.tw/upload/images/20250916/201547649l3f5TQKIh.png

今天就先到這裡 明天以這個基礎做延伸 做一個幫忙健康管理的 bot~
/images/emoticon/emoticon08.gif


上一篇
[Day 2] 群組不可缺的夥伴 - Line Bot Reminder
系列文
一路side project - 學習筆記3
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言