智慧旅行助手 Bot

基於 Google Apps Script 開發的智能旅行提醒系統,自動追蹤行程時間,提供下個景點和交通資訊的即時通知,讓旅行更加順暢。

系統特色

  • 自動化提醒:定時檢查行程,智能發送通知
  • 進度追蹤:即時計算當日行程完成百分比
  • 整合地圖:一鍵查看景點位置和交通路線
  • 多元查詢:支援今日行程、指定日期、景點詳情
  • Flex Message:美觀的 LINE 訊息設計

系統架構

Google Apps Script
  ↓
┌─────────────────┐
│ 定時執行器       │ → 檢查行程時間 → 發送通知
└─────────────────┘
       ↓
┌─────────────────┐
│ Google Sheets   │ ← 行程資料管理
└─────────────────┘
       ↓
┌─────────────────┐
│ LINE Bot API    │ ← 即時通知發送
└─────────────────┘

專案結構

travel-bot/
├── Code.gs            # 主要 Bot 邏輯
├── push.gs            # 定時推送功能
└── 行程表.xlsx         # Google Sheets 資料

核心技術實作

1. 定時檢查與通知系統

function trigger(){
  var now = new Date();
  var nowHours = now.getHours();
  var nowMinutes = now.getMinutes();
  var nowTotalMinutes = nowHours * 60 + nowMinutes;

  // 取得今天的日期並格式化
  var today = new Date();
  var formattedToday = Utilities.formatDate(today, "GMT+08:00", "yyyy/MM/dd");

  for(var i = 1; i <= LastRow - 1; i++){
    
    // 解析日期字符串
    var date = new Date(sheet.getRange(i+1,1).getValue());
    // 轉換日期格式
    var formattedDate = Utilities.formatDate(date, "GMT+08:00", "yyyy/MM/dd");

    // 比較兩個日期是否相同
    var isSameDate = (formattedDate === formattedToday);

    if(isSameDate){
      var cellValue = sheet.getRange(i+1,2).getValue();
      var formattedTime = Utilities.formatDate(cellValue, "GMT+08:00", "HH:mm");
      var timeParts = formattedTime.split(':');
      var cellHours = parseInt(timeParts[0]);
      var cellMinutes = parseInt(timeParts[1]);
      var cellTotalMinutes = cellHours * 60 + cellMinutes;
    
      // 計算時間差
      var timeDiff = cellTotalMinutes - nowTotalMinutes;

      if(timeDiff <= 60 && sheet.getRange(i+1,5).getValue() == "未傳送"){
        sheet.getRange(i+1,5).setValue("已傳送");
        var no = sheet.getRange(i+1,4).getValue();
        if(no.includes("A")){
          attractions(no);
        }
        else if(no.includes("T")){
          transportation(no);
        }
        else if(no.includes("S")){
          stay(no);
        }
      }
    }
  }
}

2. 行程進度計算

// 計算今日行程進度
var now = new Date();
var startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate());
var endOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1);

// 計算從今天開始到現在的毫秒數
var elapsed = now - startOfDay;

// 計算一天的總毫秒數
var total = endOfDay - startOfDay;

// 計算百分比
var percentage = (elapsed / total) * 100;

3. LINE Bot 訊息處理

function doPost(e) {
  var event = JSON.parse(e.postData.contents).events[0];
  var userid = event.source.userId;
  var profile = client.getProfile(userid);
  var name = profile.displayName;
  var message = event.message.text;
  var timestamp = new Date(event.timestamp);

  //log
  sheet2.getRange(LastRow2+1,1).setValue(name);
  sheet2.getRange(LastRow2+1,2).setValue(message);
  sheet2.getRange(LastRow2+1,3).setValue(timestamp);
  sheet2.getRange(LastRow2+1,4).setValue(event);

  if(message.includes("今日行程")){
    // 處理今日行程查詢
    sendTodaySchedule(event.replyToken);
  }
  else if(message.includes("/")){
    // 處理指定日期查詢
    sendDateSchedule(event.replyToken, message);
  }
  else if(message.includes("A")){
    // 處理景點查詢
    sendAttractionInfo(event.replyToken, message);
  }
}

4. Flex Message 景點卡片

function attractions(no){
  let result_array = [];
  var flex_bubble_tmp;
  var place = "";

  for(let i = 1; i <= LastRowA - 1; i++){
    if(sheetA.getRange(i + 1,1).getValue() == no){
      place = sheetA.getRange(i+1,2).getValue();

      flex_bubble_tmp = {
        "type": "bubble",
        "direction": "ltr",
        "hero": {
          "type": "image",
          "url": sheetA.getRange(i+1,4).getValue(),
          "size": "full",
          "aspectRatio": "20:13",
          "aspectMode": "cover",
        },
        "body": {
          "type": "box",
          "layout": "vertical",
          "contents": [
            {
              "type": "text",
              "text": place,
              "weight": "bold",
              "size": "xl",
              "contents": []
            },
            {
              "type": "box",
              "layout": "vertical",
              "spacing": "sm",
              "margin": "lg",
              "contents": [
                {
                  "type": "box",
                  "layout": "baseline",
                  "spacing": "sm",
                  "contents": [
                    {
                      "type": "text",
                      "text": "地址",
                      "size": "sm",
                      "color": "#AAAAAA",
                      "flex": 1,
                      "contents": []
                    },
                    {
                      "type": "text",
                      "text": sheetA.getRange(i+1,3).getValue(),
                      "size": "sm",
                      "color": "#666666",
                      "flex": 5,
                      "wrap": true,
                      "contents": []
                    }
                  ]
                }
              ]
            }
          ]
        },
        "footer": {
          "type": "box",
          "layout": "vertical",
          "flex": 0,
          "spacing": "sm",
          "contents": [
            {
              "type": "button",
              "action": {
                "type": "uri",
                "label": "地點介紹",
                "uri": sheetA.getRange(i+1,8).getValue()
              },
              "height": "sm",
              "style": "link"
            },
            {
              "type": "button",
              "action": {
                "type": "uri",
                "label": "Map",
                "uri": sheetA.getRange(i+1,5).getValue()
              },
              "height": "sm",
              "style": "link"
            }
          ]
        }
      }
    }
  }

  result_array.push(flex_bubble_tmp);
  sendLine(flex_content);
}

系統展示

LINE Bot 旅行助手示例


相關資料

🔗 Google App Script 建立 LINE Bot

📊 完整原始碼 (Google Sheet 建立副本於擴充功能查看 App Script)