2018/1/18

第三十天:卡米狗查天氣

今天就是最後一天惹,有些事情想跟你們講一下,那就是我們前幾天到底在幹嘛。 以下是一些示意圖,說明我們的 HTTP request 傳遞的路徑。 # 回覆訊息 ![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIBBLpwmEsBflGznxGYNV4CLgnla3Lh4AhGHHzDm7Y6W5DOjq2wg30F-gixpI6LsMvRfl6HaItAng8R8T5T54c9skSRBFmh72com1rUaAG6rXWbsKN4GT7vn8ebUxUhS3Xb2oGwUCUuZI/s1600/1.jpg) Line app 指的是手機或PC版的 Line,Line server 在收到訊息後會透過 webhook url 傳遞給我們。接著我們會打 `line.reply_message` 傳訊息給 Line server,最後再由 Line server 傳給 Line app (最後這段可能不是 HTTP request)。 # 發公告 ![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8T3lFxHxi4rhSMw9ndgSkirez5iQwkfbfGwicUQHLHILsTqN0EseIx5G8bxVRmIc1VK2yhzL0dxMePFlE-B6UcQfjd2jRLBl3bVafCaVEXLmWYQa7fyM0BnONZXXVRZPDenzCHGEtUV0/s1600/2.jpg) 我們透過後台管理介面填入公告訊息,用 `line.push_message` 傳訊息給 Line server。 # 排程公告 ![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhinS59WbIvMzo1jg4XW6HPlR4xBzN5OM7ZLsywJXMxVoZ-g6WYpjxXM3o4e8vr3P6zdJD2CK9rFHo0t1xravSHin4pQjp-tK2lKwKV_uJUX02mqZtEGuf0rQJHtJ_iplIs4husKWnGbaA/s1600/3.jpg) 有觀眾說想知道鬧鐘怎麼作,這裡再說明一下。 我們會用到 worker 來處理工作排程。首先是先在後台設定預約發訊息,然後將訊息儲存到工作清單,每個工作可以指定執行時間,接著就等時間到,worker 就會用 `line.push_message` 去打 Line server。 # 查天氣 ![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPy6B-QhMCDjGqgmspWGgDP5A51NwqV1Jc8J4wgIN4vXWRPa8RxuhpYycIlES-nmpsMiVO-c7OiOM9TBeTBjncQtxEc9gXAo_6vzCQ1YYCL1EHUSdTgZS-DIwMteKD1vtfi54mL_fCius/s1600/4.jpg) 查天氣就更複雜了,我們收到查天氣指令後,要先去氣象局取得圖片檔,然後再把圖上傳到 imgur,最後把圖片連結傳回給 Line server。 為什麼不是直接把氣象局的圖片傳給 Line server 呢?因為 Line server 要求圖檔必須是 https 開頭的網址,但是氣象局的圖檔連結卻是 http 開頭。 那為什麼不是我們自己保存圖片就好呢?因為存圖片要占空間跟頻寬,所以我選擇用 imgur 的空間放圖。imgur 有一個蠻好的地方是,你可以直接把圖片網址給他,他就會幫你備份圖片了,所以我們不用真的把圖檔抓回來再上傳到 imgur。 # 查天氣的運作流程 我們作簡單一點,當有人說到`天氣`的時候就傳回一張雷達回波圖。我們需要作的所有事情是: 調查階段: - 學會怎麼抓到最新的雷達回波圖網址 - 學會怎麼把圖檔弄到 imgur 實作階段: - 在主程式呼叫查天氣 - 增加一個查天氣函數 - 增加一個取得最新雷達回波圖的函數 - 增加一個上傳圖片到 imgur 的函數 - 傳送圖片到 line 的函數 一步步來吧。 # 學會怎麼抓到最新的雷達回波圖網址 當然,如果我們是用瀏覽器下載,那麼很簡單直接網頁打開`右鍵`->`另存圖片`就載好了。可是我們是要用程式去載圖,不是人工載圖。 所以我們要用程式去開啟網頁,然後從網頁原始碼裡面找到圖片連結就行了。 先開這個網頁:[http://www.cwb.gov.tw/V7/observe/radar/](http://www.cwb.gov.tw/V7/observe/radar/) 然後按下 `Ctrl`+`U`,就可以看到網頁原始碼了,把他認真的讀完之後會發現第 234~237 行很可疑,點進去看就會發現全都是圖檔連結,像這樣:[http://www.cwb.gov.tw/V7/js/HDRadar_1000_n_val.js](http://www.cwb.gov.tw/V7/js/HDRadar_1000_n_val.js)。 要能發現第 234~237 行很可疑,你必須要能看懂大部分的 html 跟 js,所以你得學會 html 跟 js。 如果你還沒學過 html 的話,可以參考看看:[深入淺出立即上手的 HTML 網頁設計](https://5xruby.tw/talks/css-html-2018-1) 如果你還沒學過 js 的話,也可以參考看看:[JavaScript & jQuery 前端開發入門實戰](https://5xruby.tw/talks/JS-jQuery-2018-1) ``` var HDRadar_1000_n_val=new Array( new Array("2018/01/18 01:20","/V7/observe/radar/Data/HD_Radar/CV1_1000_201801180120.png"), new Array("2018/01/18 01:10","/V7/observe/radar/Data/HD_Radar/CV1_1000_201801180110.png"), new Array("2018/01/18 01:00","/V7/observe/radar/Data/HD_Radar/CV1_1000_201801180100.png"), new Array("2018/01/18 00:50","/V7/observe/radar/Data/HD_Radar/CV1_1000_201801180050.png"), ... ``` 這是 js 程式碼,我們需要的部分在第二行後半段:`/V7/observe/radar/Data/HD_Radar/CV1_1000_201801180120.png`,這是網頁路徑,省略了網域的寫法。 把網域加回去就會是 [http://www.cwb.gov.tw/V7/observe/radar/Data/HD_Radar/CV1_1000_201801180120.png](http://www.cwb.gov.tw/V7/observe/radar/Data/HD_Radar/CV1_1000_201801180120.png): ![](http://www.cwb.gov.tw/V7/observe/radar/Data/HD_Radar/CV1_1000_201801180120.png) 這就是我們要的圖片連結。 ### 小結 抓 [http://www.cwb.gov.tw/V7/js/HDRadar_1000_n_val.js](http://www.cwb.gov.tw/V7/js/HDRadar_1000_n_val.js) 的原始碼,然後取出第二行的網頁路徑,最後在前面補上 `http://www.cwb.gov.tw` 就會是我們要的網址。 # 學會怎麼把圖檔弄到 imgur imgur 有提供 api,這是說明文件:[https://apidocs.imgur.com/#4b8da0b3-3e73-13f0-d60b-2ff715e8394f](https://apidocs.imgur.com/#4b8da0b3-3e73-13f0-d60b-2ff715e8394f)。 使用 api 需要 Client-ID,這東西就跟 Line channel secret 那些東西差不多。 你可以透過這個網址:[https://api.imgur.com/oauth2/addclient](https://api.imgur.com/oauth2/addclient) 取得你的 Client-ID。 ![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjer94avY65FjgMHD2HDhxK5GuUvm2ribzg_WSWlw4hx7azCULh3E7N81JzOymhCHracAx72jWM8Ue5oSHaS9saoU1wXpPAfAnCkJmkk5DsSUImBZnp0BsibSAnVK15-_ntYHA9wtJoXU0/s1600/6.jpg) 照著填就可以。 # 小結 透過使用 imgur 提供的 api,我們可以很容易就上傳圖片到 imgur。 接下來是實作階段的部分。 # 在主程式呼叫查天氣 ``` def webhook # 查天氣 reply_image = get_weather(received_text) # 有查到的話 後面的事情就不作了 unless reply_image.nil? # 傳送訊息到 line response = reply_image_to_line(reply_image) # 回應 200 head :ok return end # 紀錄頻道 Channel.find_or_create_by(channel_id: channel_id) # 學說話 reply_text = learn(channel_id, received_text) # 關鍵字回覆 reply_text = keyword_reply(channel_id, received_text) if reply_text.nil? # 推齊 reply_text = echo2(channel_id, received_text) if reply_text.nil? # 記錄對話 save_to_received(channel_id, received_text) save_to_reply(channel_id, reply_text) # 傳送訊息到 line response = reply_to_line(reply_text) # 回應 200 head :ok end ``` 我在最前面加入了這段程式碼: ``` # 查天氣 reply_image = get_weather(received_text) # 有查到的話 後面的事情就不作了 unless reply_image.nil? # 傳送訊息到 line response = reply_image_to_line(reply_image) # 回應 200 head :ok return end ``` 我們要作一個查天氣函數 `get_weather` 如果輸入的文字包含`天氣`,就傳回 https 的雷達回波圖網址,然後就將圖片傳回給 line,這裡因為之前都是傳文字而已,所以還要多作一個函數 `reply_image_to_line` 來傳圖片。 # 增加一個查天氣函數 ``` def get_weather(received_text) return nil unless received_text.include? '天氣' imgur(get_weather_from_cwb) end ``` 第一行是說如果輸入的文字不包含天氣,就傳回 nil。 第二行呼叫了兩個函數,第一個函數是 `get_weather_from_cwb`,這是取得雷達回波圖的函數,會得到一個網址,再把這個網址傳給 `upload_to_imgur` 這個上傳圖片到 imgur 的函數。 # 增加一個取得最新雷達回波圖的函數 在[第十六天:做一個最簡單的爬蟲](ttps://ithelp.ithome.com.tw/articles/10195760)學到的在 rails 發 HTTP request 跟在[第二十五天:卡米狗學說話](https://ithelp.ithome.com.tw/articles/10197013)學到的字串處理又要派上用場了,就跟你說前面的文章都是在打基礎吧,漏掉一篇你就做不出來了。 ``` def get_weather_from_cwb uri = URI('http://www.cwb.gov.tw/V7/js/HDRadar_1000_n_val.js') response = Net::HTTP.get(uri) start_index = response.index('","') + 3 end_index = response.index('"),') - 1 "http://www.cwb.gov.tw" + response[start_index..end_index] end ``` 前兩行就是第十六天講過的,後三行就是第二十五天講過的。比較難懂的可能會是第三行跟第四行,先看一下這張圖: ![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0ou1oNmbu3Vsu744XKQ6rLhJwcb5HOUktAAbRDXY6UAAfZZg5dOb4hxcZBPh47j0gk6LL_ecyO7bRN8lW9ooM9vnoL61k3dPnOAdCwdmyoN0_gnz-a0j3PzYku3GlHlKE7o-DB9Ed_7U/s1600/5.jpg) 總而言之就是網址的開頭前面是 `","` 後面是 `"),` 如果你有學過 js 應該就會知道,這個開頭跟結尾應該是不會錯的,所以我們決定取出介於這中間的字。 這行是在抓起點: ``` start_index = response.index('","') + 3 ``` 這是在抓終點: ``` end_index = response.index('"),') - 1 ``` # 增加一個上傳圖片到 imgur 的函數 ``` def upload_to_imgur(image_url) url = URI("https://api.imgur.com/3/image") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["authorization"] = 'Client-ID be2d83405627ab8' request.set_form_data({"image" => image_url}) response = http.request(request) json = JSON.parse(response.read_body) begin json['data']['link'].gsub("http:","https:") rescue nil end end ``` 我們設定好 request header 和 request body 之後打一個 post request 出去,他會返回一個 json,接著我作了 json 的解析,並且在解析失敗時傳回 nil,確保程式不會隨意掛點。 ``` request["authorization"] = 'Client-ID be2d83405627ab8' ``` 這行是要填入你自己的 Client-ID,`be2d83405627ab8` 是我亂打的。 # 傳送圖片到 line 的函數 ``` # 傳送圖片到 line def reply_image_to_line(reply_image) return nil if reply_image.nil? # 取得 reply token reply_token = params['events'][0]['replyToken'] # 設定回覆訊息 message = { type: "image", originalContentUrl: reply_image, previewImageUrl: reply_image } # 傳送訊息 line.reply_message(reply_token, message) end ``` 其實跟傳文字幾乎一樣,只差在 message 裡面不一樣而已。 # 上傳實測 ![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhS1WcxYPIl9VoOutCXhiiZtKxCbxAf1IR3fhzmnYiJ6LJU190f9GFfZOEFf9_xNnjQTL6qr0_odQiqQ0OXxrg6EdRmoXY3LQIJdODgq2C1zH1VylSCSnnOZ6XXzwZMyktsmpYnYkiVPf0/s1600/7.jpg) 成功! # 本日重點 - 學會抓雷達回波圖 - 學會用 imgur api - 學會傳圖片到 line - 想要學會作爬蟲,就要學會 html 跟 js - 如果你還沒學過 html 的話,可以參考看看:[深入淺出立即上手的 HTML 網頁設計](https://5xruby.tw/talks/css-html-2018-1) - 如果你還沒學過 js 的話,也可以參考看看:[JavaScript & jQuery 前端開發入門實戰](https://5xruby.tw/talks/JS-jQuery-2018-1) 如果你原本是完全不會寫程式,你從第一篇一直看到這篇,最後有作出東西的話,請在底下留言:「感恩卡米,讚嘆卡米」,讓我能證明`只要有心,人人都可以作卡米狗`是真的。

沒有留言: