自動降價通知系統
使用 Google Apps Script 實作的即時商品價格監控系統,整合 LINE Bot 實現自動化降價通知,畢竟價格下降,就是降價了。
系統特色
- 自動監控:Google Apps Script 定時執行器全天監控價格
- 即時通知:LINE Bot 整合,降價第一時間推播通知
- 商品追蹤:MOMO 、 PChome 商品價格追蹤
- Flex Message:降價通知訊息設計
- 完全免費:無需付費訂閱或伺服器費用
系統架構
使用者 (LINE Bot)
↓
┌─────────────────┐
│ Google Apps │ → 定時執行器 → 商品價格監控
│ Script │
└─────────────────┘
↓
┌─────────────────┐
│ Google Sheets │ ← 使用者資料與價格紀錄
└─────────────────┘
↓
┌─────────────────┐
│ LINE Messaging │ ← 降價通知發送
│ API │
└─────────────────┘
專案結構
price-alert/
├── Code.gs # LINE Bot 主要邏輯
├── updata.gs # 價格監控與通知
└── List.xlsx # Google Sheets
核心技術實作
1. 商品資訊擷取
function fetchData(iCode) {
var url = "https://www.momoshop.com.tw/goods/GoodsDetail.jsp?i_code=" + iCode;
var response = UrlFetchApp.fetch(url);
var content = response.getContentText();
// 產品名稱
var titleRegex = /<meta property="og:title" content="([^"]+)">/;
var titleMatch = content.match(titleRegex);
var productName = titleMatch ? titleMatch[1] : "未找到產品名稱";
// 產品價格
var priceRegex = /<meta property="product:price:amount" content="([^"]+)"/;
var priceMatch = content.match(priceRegex);
var productPrice = priceMatch ? priceMatch[1] : "未找到產品價格";
Logger.log("產品名稱: " + productName);
Logger.log("產品價格: " + productPrice);
var goods = [];
goods.push(productName);
goods.push(productPrice);
Logger.log(goods);
return goods
}
2. 價格比較與監控
function compareData() {
var msg = "";
for (var i=1;i<LastRowU;i++){
var userid = sheetU.getRange(i+1,1).getValue();
var sheetP = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(userid);
var LastRowP = sheetP.getLastRow();
for(var j=1;j<LastRowP;j++){
var iCode = sheetP.getRange(j+1,1).getValue();
var product = fetchData(iCode);
if(product[1] != sheetP.getRange(j+1,3).getValue()){
if(product[1] > sheetP.getRange(j+1,3).getValue()){
msg = "漲價了"
sheetP.getRange(j+1,3).setValue(product[1]);
}
else if(product[1] < sheetP.getRange(j+1,3).getValue()){
msg = "降價了"
sheetP.getRange(j+1,3).setValue(product[1]);
sendLine(userid,iCode,msg,product);
}
}
}
}
}
3. LINE Bot 訊息處理
function doPost(e) {
let 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 date = new Date(event.timestamp);
sheet.getRange(LastRow+1,1).setValue(name);
sheet.getRange(LastRow+1,2).setValue(message);
sheet.getRange(LastRow+1,3).setValue(date);
sheet.getRange(LastRow+1,4).setValue(event);
if(message == "加入會員"){
var users = [];
var count = 0;
var text = "";
for (var i=1;i<LastRowU;i++){
users.push(sheetU.getRange(i+1,1).getValue());
if(userid == sheetU.getRange(i+1,1).getValue()){
count = count + 1;
}
}
if(count >= 1){
text = "已加入,無須再次點擊";
}
else{
sheetU.getRange(LastRowU+1,1).setValue(userid);
text = "加入成功";
ss.insertSheet(userid);
var sheetP = ss.getSheetByName(userid);
sheetP.getRange(1, 1).setValue("iCode").setBackground("#ffe599").setFontWeight("bold").setHorizontalAlignment("center");
sheetP.getRange(1, 2).setValue("productName").setBackground("#ffe599").setFontWeight("bold").setHorizontalAlignment("center");
sheetP.getRange(1, 3).setValue("productPrice").setBackground("#ffe599").setFontWeight("bold").setHorizontalAlignment("center");
}
var echo = {
type: 'text',
text: text
};
client.replyMessage(event.replyToken, echo);
}
else if (message.includes("momoshop.com.tw")){
var text = "";
var iCode = extractParameterFromUrl(message, "i_code");
try{
var sheetP = ss.getSheetByName(userid);
var LastRowP = sheetP.getLastRow();
var rangeP = sheetP.getRange(1, 1, LastRowP, 1);
var valuesP = rangeP.getValues();
// 檢查是否有重複的 iCode
var isDuplicate = false;
for (var i = 0; i < valuesP.length; i++) {
if (valuesP[i][0] == iCode) {
isDuplicate = true;
break;
}
}
if (isDuplicate) {
text = "重複商品";
} else {
sheetP.getRange(LastRowP + 1, 1).setValue(iCode);
var goods = fetchData(iCode);
sheetP.getRange(LastRowP + 1, 2).setValue(goods[0]);
sheetP.getRange(LastRowP + 1, 3).setValue(goods[1]);
text = "商品添加成功\n商品名稱 : " + goods[0] + "\n商品價格 : " + goods[1];
}
}
catch(e){
text = "尚未加入會員";
}
var echo = {
type: 'text',
text: text
};
}
client.replyMessage(event.replyToken, echo);
}
4. Flex Message 降價通知
function sendLine(userid,iCode,msg,product){
var uuu = "https://www.momoshop.com.tw/goods/GoodsDetail.jsp?i_code=" + iCode;
var date = Utilities.formatDate(new Date(), SpreadsheetApp.getActive().getSpreadsheetTimeZone(), "yyyy/MM/dd HH:mm");
var url = 'https://api.line.me/v2/bot/message/push';
UrlFetchApp.fetch(url, {
'headers': {
'Content-Type': 'application/json; charset=UTF-8',
'Authorization': 'Bearer ' + CHANNEL_ACCESS_TOKEN,
},
'method': 'post',
'payload': JSON.stringify({
'to': userid,
'messages': [{
"type": "flex",
"altText": msg.substring(0,2) + "通知",
"contents":{
"type": "bubble",
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": product[0] + msg,
"weight": "bold",
"size": "xl"
},
{
"type": "box",
"layout": "vertical",
"margin": "md",
"contents": [
{
"type": "text",
"text": "價格 : " + product[1],
"size": "sm",
"margin": "md",
"flex": 0,
"weight": "bold"
}
]
},
{
"type": "box",
"layout": "vertical",
"margin": "lg",
"spacing": "sm",
"contents": [
{
"type": "box",
"layout": "baseline",
"spacing": "sm",
"contents": [
{
"type": "text",
"text": "Time",
"color": "#aaaaaa",
"size": "sm",
"flex": 1
},
{
"type": "text",
"text": date,
"wrap": true,
"color": "#666666",
"size": "sm",
"flex": 5
}
]
}
]
}
]
},
"footer": {
"type": "box",
"layout": "vertical",
"spacing": "sm",
"contents": [
{
"type": "button",
"style": "link",
"height": "sm",
"action": {
"type": "uri",
"label": "商品連結",
"uri": uuu
}
},
{
"type": "box",
"layout": "vertical",
"contents": [],
"margin": "sm"
}
],
"flex": 0
}
}
}]
})
}
)
}
系統展示
也可使用Discord Webhook
相關資料
📊 完整原始碼 (Google Sheet 建立副本於擴充功能查看 App Script)