用line push messaging api推播每日氣象預報

用line push messaging api推播每日氣象預報

2019/06/25補充:
自從LINE@改成2.0後,原本用的開發用方案一律轉成輕量型,即便是用程式自動推播,也算進訊息數裡,而輕量型一個月最多就500則。
因此很遺憾的,自動推播功能得取消,目前這篇實作出的機器人,會是自動回覆用,像是回覆當地天氣、回覆目前google trend、回覆目前幾個新聞的rss……等,程式碼一樣可以執行,但在排程的部份將會取消。

2019/07/03補充:
推播功能逐步移至Telegram機器人上,可看這篇:

https://medium.com/@augustus0818/telegram-bot-api-1-e4ea74d3b064


本篇用到的資源

實作完上一篇的「用LINE bot api建立line@圖文選單」以後,開始對line api的說明文件有些了解,這篇紀錄的是實作一個推播天氣機器人的甘苦路。

實作一個「每日早上7點,自動推播天氣預報」的機器人,用了以下資源:

放個廣告賺點養主機的$$,謝謝

  • 程式語言:node.js
  • 主機:google cloud platform
  • 排程:GCP cron
  • 資料庫:firebase
  • 氣象資料:中央氣象局

之前陸陸續續寫的幾篇筆記文,就是在為這個功能做準備,可以參考:

node.js / GCP:用Google Cloud Platform(GCP)建node.js網站
氣象資料:接氣象局api、跨網域get資料
資料庫:如果對firebase不熟,也可以考慮 如何用Google Excel當作資料庫

這篇不會像之前幾篇一樣,完整的列出所有程式碼,因為實作完後,總共寫了四百多行,都是血淚,但會寫出幾個踩到的坑及要注意的事。

先提供實作後的成果:

https://line.me/R/ti/p/%40gnp0209t


npm init完後,要安裝4個套件:

  • express
  • request:跟氣象局要資料
  • line bot sdk nodejs:LINE push
  • firebase admin:存取firebase資料庫
npm install express request @line/bot-sdk firebase-admin --save

安裝完後,開一個index.js引用上面4個套件:


拿訂閱者的基本資料

當使用者傳訊息給line的機器人,line api會回傳幾項資訊,標粗體的就是需要存起來的:

{
  "destination": "xxxxxxxxxx", 
  "events": [
    {
      "replyToken": "0f3779fba3b349968c5d07db31eab56f",
      "type": "message",
      "timestamp": 1462629479859,
      "source": {
        "type": "user",
        "userId": "U4af4980629..."
      },
      "message": {
        "id": "325708",
        "type": "text",
        "text": "Hello, world"
      }
    }
  ]
}

replyToken直接存成變數就行,不用存進資料庫。

放個廣告賺點養主機的$$,謝謝

message.text看個人,這篇是拿使用者打的字當關鍵字,讓機器人讀到後可以自動回覆用的,所以只在使用者選擇了地區後才會存結果。

source.userId,這個必存,他就像web push時的token一樣,是要發送對象的id,沒有這個id,push時會沒有目標可以push。

從line的get profile文件中可以知道,有了userId以後,可以拿到使用者的公開資訊,一共有3個:名稱、大頭照、個人訊息:

{
    "displayName": "LINE taro",
    "userId": "U4af4980629...",
    "pictureUrl": "https://obs.line-apps.com/...",
    "statusMessage": "Hello, LINE!"
}

userId,是這個機器人給這個帳號的id,所以每個帳號對不同的機器人會有不同id。

這一步驟的流程如下:

  • 當使用者傳了訊息給機器人 →
  • 拿到userId →
  • 用userId拿公開資料 →
  • 存入firebase

前3步會用到line reply api,這部份可以直接看說明文件的範例碼:

ttps://line.github.io/line-bot-sdk-nodejs/guide/webhook.html#error-handling

第四步存到firebase的資料架構如下:

firsebase_id: {
  userId: {
    name: encodeURIComponent(line.displayName),
    img: line.pictureUrl,
    status: encodeURIComponent(line.statusMessage),
    location: encodeURIComponent('預設地區')
  }
}

拿userId當key,是之後在跑迴圈發push時,抓資料的地方比較單純,不用一層一層找。

name、status、location這3個,因為值通常是中文,所以編碼後再存。

*踩坑處:userLocation的部份,要先提供一個預設值,以免有人沒填地區,回傳的值變成undefined時,會讓迴圈卡住,造成push失敗。

實際存到firebase的結果:

存userId、公開資料進到firebase
存userId、公開資料進到firebase

存使用者選擇的地區

這步驟的流程如下:

  • 用圖文選單讓使用者發送「我想選擇預報地區」 →
  • 回傳carousel樣式,讓使用者選擇地區 →
  • 當使用者發送的訊息有「區」這個字以後,更新到firebase

建立圖文選單的部份,可以直接用line@後台製作。也可以用api設定(用LINE bot api建立line@圖文選單)。

當message.text是「我想選擇預報地區」,用reply api去回傳訊息讓使用者點選地區,replay api就一行:

client.replyMessage(event.replyToken, message);

message部份,就是一個物件,比較特別的是,用line的後台發訊息,格式很固定,但如果是用api的方式發,樣式就多了起來。

除了一般的文字、圖片,用api的方式還有2種很特別的:template、flex。

這2種樣式,怕截圖下來如果因為侵權被吉就糟了,所以直接附上網址,可以點進去看:

https://developers.line.biz/en/docs/messaging-api/message-types/#template-messages

這2種物件的寫法,說明文件如下:

template message
flex message

這篇選用carousel來製作地區選單,格式如下:

在line上呈現的結果如圖:

選擇地區的選單
選擇地區的選單

從氣象局拿資料

取資料的方式在「接氣象局api、跨網域get資料」這篇就有說明。

這段就寫取哪些資料,跟整理完後的資料格式。

每日天氣預報的部份,預計要發送的資料有2:氣溫、降雨機率。

所以用氣象局api拿的資料就這兩個。

在時間的參數上,因為主要是想讓大家出門上班前,可以決定要不要帶傘,及要不要穿外套,所以時間是抓0600–2100這段時間,包含了大部份上下班的時間。

氣象局的api,氣溫跟降雨機率給的時間參數是不一樣的,因為區間也不同。

  • 降雨機率是每6小時為一段,氣溫則是每3小時。
  • 降雨用的是startTime/endTime,氣溫用的是dataTime。

因為兩者參數不同,所以在request時也要分開給,最後的處理方式是:

  • 用一個function處理「今天」的日期 →
  • 拿「今天」的日期去寫進requset的url裡 →
  • 整理資料

在處理「今天」的function,有2個坑:

月、日的部份,氣象局的要是2位數,因此要記得補0。

function如下:

這個index.js裡,要用到「今天」的日期時,就用today這個變數就行。

主機所在地會影響new Date的日期

因為用的是GCP的主機,為了一年後還有免費額度存在,所以主機所在地選擇了美洲。但美洲跟台灣是有時差的,上面的funciton,在get Date()時會出現美洲的時間,因此必須寫好時區。

方法是在index.js裡加一行:

process.env.TZ = 'Asia/Taipei';

這行必須在get Date()之前。

一直寫到現在,整份index.js的架構如下:

整理氣象資料後的結果:

氣象資料整理後
氣象資料整理後

以台北市各區當key,push到各人時比較好抓資料。


LINE push api

LINE push api很簡單,就一行:

client.pushMessage(userId, message);

userId在上面就有取到,並且存在firebase了,只要把firebase的資料抓下來,用迴圈就可以抓完所有存到的userId。

推播的message,選用bubble,最後會出現的樣式如圖:

氣象推播訊息
氣象推播訊息

建立排程

當存了userId,也寫好要push的message object後,最後一步就是建一個排程,定時每日早上7點執行function,push天氣預報了。

一開始排程是用node.js的一個套件,但後來看了一下GCP的後台,發現GCP本身就有提供排程功能,免費版的可以設定20個排程。

進到GCP的後台,到專案的資訊主頁,可以看到選單上有一個「Cron工作」:

GCP Cron工作
GCP Cron工作

有設排程的話,可以看到排程的設定。

排程無法在後台用UI來設定,需要上傳一個cron.yaml到GCP上。

首先在跟app.yaml同層的位置新增一個cron.yaml,檔案內容如下:

url就是要執行的網址,index.js就寫:

app.get('/weather', (req, res) => {
  triggerPushWeather();
  res.send('succsee');
});

triggerPushWeather裡就是寫拿資料、推播的內容,就會執行這個function了。

cron.yaml建立完後,這個檔案要另外傳到GCP上,用gcloud cli的命令如下:

gcloud app deploy cron.yaml

按下enter,在按y確認,就會傳到GCP上,成功的話,再進到GCP →Cron工作,就會看見排程在上面:

排程設定成功,在後台可以看到結果
排程設定成功,在後台可以看到結果

之後每天早上7點,就會執行function,推播天氣預報到有加入好友並且選擇地區的使用者了。


北北基天氣如何呢?這邊加:

https://line.me/R/ti/p/%40gnp0209t


Summary
用line push messaging api推播每日氣象預報
Article Name
用line push messaging api推播每日氣象預報
Description
本篇大綱:本篇用到的資源、安裝所需套件、拿訂閱者的基本資料、存使用者選擇的地區、從氣象局拿資料、LINE push api、建立排程。這篇紀錄的是實作一個推播天氣機器人的甘苦路。實作一個「每日早上7點,自動推播天氣預報」的機器人。之前陸陸續續寫的幾篇筆記文,就是在為這個功能做準備。
Augustus
Let's Write
Let's Write
Publisher Logo

留言