## 前言 由於Line Notify過期,突然想到用Line BOT本來就能達成相近的目的 因此嘗試實作,並搭配網路上的Google行事曆教學 # 前置作業 ## Line Bot [申請網站Line](https://account.line.biz/login?redirectUri=https%3A%2F%2Fdevelopers.line.biz%2Fconsole%2Fchannel%2F2007174340%2Fmessaging-api%3Fstatus%3Dsuccess) Line Developers 建立provider跟Messaging API(Line BOT) ![image](https://hackmd.io/_uploads/SJegdzsKZee.png) ![image](https://hackmd.io/_uploads/Sk2ymitblx.png) ![image](https://hackmd.io/_uploads/SybofoKblx.png) 帳號不用認證也沒關係 進入管理Line帳號頁面=>設定=>右側的Messaging API ![image](https://hackmd.io/_uploads/BkrM4jFWee.png) 啟用Messaging API =>選擇自己建立的provider=>同意=>確定=>確定 產生的<font color="#f00">**Channel secret**</font>會用到 ![image](https://hackmd.io/_uploads/H1mhNoYZle.png) 回到Line Developers(非Line BOT管理頁面) 重新整理並從Channel找到自己的BOT ![image](https://hackmd.io/_uploads/BkDZHotWgx.png) 選擇後切到Messaging API ![image](https://hackmd.io/_uploads/ryGNSoYWeg.png) 滑到最下方有個Channel access token=>按下Issue ![image](https://hackmd.io/_uploads/SyDSHjYZll.png) 產生新的<font color="#f00">**Channel access token**</font>會用到 ## Google 行事曆 左側我的日曆 選擇自己想要通知的項目 設定和共用 ![image](https://hackmd.io/_uploads/By_ApdtWle.png) 整合日曆內可以看到自己的<font color="#f00">**日曆ID**</font>,也會用到 ![image](https://hackmd.io/_uploads/BJq88jKWxg.png) ## Apps Script ![image](https://hackmd.io/_uploads/HyryvjYbll.png) 從Google試算表中建立Apps Script=>建立新的試算表=>擴充功能=>Apps Script 試算表不能關掉(關掉後你的Apps Script也會關掉) ![image](https://hackmd.io/_uploads/rkEHwjK-ge.png) 程式碼 ``` /** * 取得接下來一小時內的日曆事件 * @returns {GoogleAppsScript.Calendar.CalendarEvent[]} 接下來一小時的日曆事件列表 */ function getCalendarEventsForNextHour() { var now = new Date(); // 獲取現在的時間 var oneHourLater = new Date(now.getTime() + 60 * 60 * 1000); // 計算一小時後的時間(60分鐘 * 60秒 * 1000毫秒) var events = calendar.getEvents(now, oneHourLater); // 獲取在此時間範圍內的所有事件 return events; } // LINE Bot 的認證資訊 var CHANNEL_ACCESS_TOKEN = PropertiesService.getScriptProperties().getProperty('CHANNEL_ACCESS_TOKEN'); // 從腳本屬性中獲取 LINE Channel 訪問令牌 // Google Calendar API 設定 var calendar = CalendarApp.getCalendarById(PropertiesService.getScriptProperties().getProperty('CALENDAR_ID')); // 從腳本屬性中獲取要使用的日曆 ID /** * 獲取已發送提醒的記錄 * @returns {Array} 已發送提醒的記錄列表 */ function getSentReminders() { var sentRemindersData = PropertiesService.getScriptProperties().getProperty('sentRemindersData'); return sentRemindersData ? JSON.parse(sentRemindersData) : []; // 如果有資料則解析,否則返回空陣列 } /** * 保存已發送提醒的記錄 * @param {Array} reminders - 提醒記錄列表 */ function saveSentReminders(reminders) { PropertiesService.getScriptProperties().setProperty('sentRemindersData', JSON.stringify(reminders)); } /** * 檢查並發送日曆事件提醒 * 此函數應該被設置為定時觸發器來定期執行 */ function sendCalendarReminders() { var events = getCalendarEventsForNextHour(); // 獲取接下來一小時內的事件 var sentReminders = getSentReminders(); // 獲取已發送的提醒記錄 var now = new Date().getTime(); // 當前時間戳 var twoHoursAgo = now - 2 * 60 * 60 * 1000; // 兩小時前的時間戳 // 清理過期的提醒記錄(兩小時前發送的) sentReminders = sentReminders.filter(function(reminder) { return reminder.sentTime > twoHoursAgo; }); var remindersToSend = []; // 待發送的提醒列表 events.forEach(function(event) { var eventId = event.getId(); // 獲取事件 ID var eventStartTime = event.getStartTime().getTime(); // 獲取事件開始時間的時間戳 var oneHourBefore = eventStartTime - 60 * 60 * 1000; // 計算事件開始前一小時的時間戳 // 檢查是否已發送過提醒 var alreadySent = sentReminders.some(function(reminder) { return reminder.eventId === eventId; }); // 檢查事件是否即將在一小時內開始,且尚未發送提醒 if (eventStartTime > now && !alreadySent) { // 添加到待發送提醒列表 remindersToSend.push({ eventId: eventId, title: event.getTitle(), startTime: eventStartTime }); // 記錄此提醒已發送 sentReminders.push({ eventId: eventId, sentTime: now }); } }); // 如果有需要發送的提醒 if (remindersToSend.length > 0) { // 格式化每個提醒訊息 var reminderMessages = remindersToSend.map(function(reminder) { return '活動 "' + reminder.title + '" 將於 ' + Utilities.formatDate(new Date(reminder.startTime), Session.getScriptTimeZone(), "yyyy-MM-dd HH:mm") + ' 開始!'; }); // 組合完整的提醒訊息 var message = '行事曆提醒:\n\n' + reminderMessages.join('\n\n'); // 使用廣播功能發送提醒給所有用戶 var result = broadcastToRecentInteractions(message); Logger.log('已發送行事曆提醒: ' + result); // 記錄到日誌 } // 保存更新後的提醒記錄 saveSentReminders(sentReminders); } /** * 向所有用戶廣播 LINE 訊息 * @param {string} message - 要廣播的訊息內容 * @returns {string} 廣播結果訊息 */ function broadcastToRecentInteractions(message) { var url = 'https://api.line.me/v2/bot/message/broadcast'; // LINE 廣播 API 端點 var headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + CHANNEL_ACCESS_TOKEN // 設置認證標頭 }; var payload = { 'messages': [{ 'type': 'text', 'text': message || '這是一則廣播訊息!' // 如果未提供訊息內容,則使用預設訊息 }] }; var options = { 'method': 'post', 'headers': headers, 'payload': JSON.stringify(payload) // 將負載轉換為 JSON 字串 }; var response = UrlFetchApp.fetch(url, options); // 發送 HTTP 請求到 LINE API Logger.log('廣播訊息已成功發送!'); // 記錄訊息到日誌 return '廣播訊息已成功發送!'; } /** * 測試廣播功能 * @returns {string} 廣播結果訊息 */ function testBroadcast() { var message = "這是一則測試廣播訊息,發送時間: " + new Date().toLocaleString(); var result = broadcastToRecentInteractions(message); Logger.log("廣播測試結果: " + result); // 記錄到日誌 return result; } ``` 專案設定=>指令碼屬性=>編輯指令碼屬性=>新增指令碼屬性 ![image](https://hackmd.io/_uploads/rJ-O_oKWge.png) CALENDAR_ID(建議用自己的信箱,能不能用他人的信箱待驗證) CHANNEL_ACCESS_TOKEN CHANNEL_SECRET 觸發條件=>新增觸發條件 ![image](https://hackmd.io/_uploads/BJLp_otbgx.png) ## Note!! 如果之後如下發現開不起來的話(Apps Script)得用無痕視窗,刪除cookie沒用 ![image](https://hackmd.io/_uploads/BJ0-qoF-gx.png) [參考教學](https://weijutu.github.io/2020/04/13/javascript/calendar-line-notify/)