2018/1/17

第二十九天:卡米狗發公告

今天我們要作的是主動傳訊息的功能。 目前我們用到的都只是回覆訊息的功能: # 認識 Push Message API ``` # 傳送訊息到 line def reply_to_line(reply_text) return nil if reply_text.nil? # 取得 reply token reply_token = params['events'][0]['replyToken'] # 設定回覆訊息 message = { type: 'text', text: reply_text } # 傳送訊息 line.reply_message(reply_token, message) end ``` 上面這個函數是我們之前寫好的 `reply_to_line` 函數,裡面的最後一行: ``` line.reply_message(reply_token, message) ``` 這是在呼叫 line 提供給我們的回覆訊息函數,而 line 也有提供讓我們主動發訊息的函數: ``` response = line.push_message(channel_id, message) ``` 我們需要傳遞 channel_id,告訴 Line 誰應該收到這個訊息,channel_id 就是 userId, groupId 或 roomId。所以我們需要一個資料模型去保存所有頻道的 channel_id。 文件參考在這裡:[https://developers.line.me/en/docs/messaging-api/reference/#send-push-message](https://developers.line.me/en/docs/messaging-api/reference/#send-push-message) # 保存所有頻道 ### 建立資料模型 ``` rails g model channel channel_id ``` 建立一個資料表叫作 channel,裡面有個欄位叫作 channel_id。 ### 資料庫遷移 ``` rails db:migrate ``` bj4 ### 儲存頻道 在主程式中加入一行: ``` Channel.create(channel_id: channel_id) ``` 如果你覺得是這樣寫,那你就錯了,因為這樣會導致相同的資料會一直被存進去,到時候你發公告,同一個人就會收到超多次。 ``` Channel.find_or_create_by(channel_id: channel_id) ``` 先看有沒有相同的資料,如果已經有資料的話就不寫入。如果沒有資料才作寫入。這邊有詳細的說明:[https://rails.ruby.tw/active_record_querying.html#find-or-create-by](https://rails.ruby.tw/active_record_querying.html#find-or-create-by) 加入後的主程式長這樣: ``` def webhook # 紀錄頻道 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 ``` 接下來要作一個後台網頁去發這個公告。我們會需要兩個 Action,一個 get new Action 用來顯示發公告的後台頁面,另一個 post create Action 用來接收發公告訊息的請求。 # 管理後台 這次我們手動新增,不使用產生器。 ### 加 Route 修改 `config/routes.rb`,新增一行: ``` resources :push_messages, only: [:new, :create] ``` 這是加入一組資源,但我們只使用其中的 new 和 create。 ### 加 Controller 在 `app/controllers` 資料夾下建立一個叫作 `push_messages_controller.rb` 的檔案: ``` class PushMessagesController < ApplicationController before_action :authenticate_user! # GET /push_messages/new def new end # POST /push_messages def create end end ``` 我們檢查使用者必須先登入,然後開了兩個空的 Action,之後再回頭來改。 ### 加 View 我們要在 `app/views/push_messages` 下新增一個檔案 `new.html.erb`。 這是 `new.html.erb` 所需要的全部程式碼: ``` <%= form_with(url: '/push_messages', local: true) do |form| %> <%= text_area_tag 'text' %> <%= submit_tag "送出" %> <% end %> ``` 一個表單的開始是 `<%= form_with ..... do ... %>`,結束是 `<% end %>`。表單預設是用 post 方法,所以就不用特別寫出來。 `<%= text_area_tag 'text' %>` 是輸入文字框。 `<%= submit_tag "送出" %>` 則是送出按鈕。 如果要了解更多的話可以參考:[Action View 表單輔助方法](https://rails.ruby.tw/form_helpers.html) ### 改 Controller 我們在接收到請求之後要作發訊息的動作: ``` def create text = params[:text] Channel.all.each do |channel| push_to_line(channel.channel_id, text) end redirect_to '/push_messages/new' end ``` `text = params[:text]` 這是取得剛剛在輸入文字框填的文字 `Channel.all.each do |channel|` ... `end` 這段是指我們想要對每一個 channel 作一些事情。 `push_to_line(channel.channel_id, text)` 這是說我們要主動發訊息 `text` 給頻道 `channel.channel_id`,這個函數我們待會才會寫。 ### push_to_line ``` # 傳送訊息到 line def push_to_line(channel_id, text) return nil if channel_id.nil? or text.nil? # 設定回覆訊息 message = { type: 'text', text: text } # 傳送訊息 line.push_message(channel_id, message) end ``` 長得跟之前的 `reply_to_line` 有 87% 像,就不解釋了。 ### 對一下程式碼 完整的 `push_messages_controller.rb` 應該長這樣: ``` require 'line/bot' class PushMessagesController < ApplicationController before_action :authenticate_user! # GET /push_messages/new def new end # POST /push_messages def create text = params[:text] Channel.all.each do |channel| push_to_line(channel.channel_id, text) end redirect_to '/push_messages/new' end # 傳送訊息到 line def push_to_line(channel_id, text) return nil if channel_id.nil? or text.nil? # 設定回覆訊息 message = { type: 'text', text: text } # 傳送訊息 line.push_message(channel_id, message) end # Line Bot API 物件初始化 def line @line ||= Line::Bot::Client.new { |config| config.channel_secret = '9160ce4f0be51cc72c3c8a14119f567a' config.channel_token = '2ncMtCFECjdTVmopb/QSD1PhqM6ECR4xEqC9uwIzELIsQb+I4wa/s3pZ4BH8hCWeqfkpVGVig/mIPDsMjVcyVbN/WNeTTw5eHEA7hFhaxPmQSY2Cud51LKPPiXY+nUi+QrXy0d7Hi2YUs65B/tVOpgdB04t89/1O/w1cDnyilFU=' } end end ``` # 發布和測試 自己試試看,你們需要練習自己解決卡關,要養成看 log 的習慣。 # 關於怎麼做鬧鐘 你需要把主動發訊息這件事情加入排程,請參考:[Active Job 基礎](https://rails.ruby.tw/active_job_basics.html#%E4%BB%BB%E5%8B%99%E6%8E%92%E7%A8%8B) 以及 [Delayed Job (DJ)](https://devcenter.heroku.com/articles/delayed-job)。 自己摸這個要有花上一週的心理準備,加油加油加油,你是最棒的,耶! # 本日重點 - 學會記錄頻道 - 學會主動發訊息 - 學會寫 view 的表單 沒意外的話,明天就講怎麼查天氣。

沒有留言: