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)

我們需要傳遞 channelid,告訴 Line 誰應該收到這個訊息,channelid 就是 userId, groupId 或 roomId。所以我們需要一個資料模型去保存所有頻道的 channel_id。

文件參考在這裡: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/activerecordquerying.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 表單輔助方法

改 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,這個函數我們待會才會寫。

pushtoline

  # 傳送訊息到 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 基礎 以及 Delayed Job (DJ)

自己摸這個要有花上一週的心理準備,加油加油加油,你是最棒的,耶!

本日重點

  • 學會記錄頻道
  • 學會主動發訊息
  • 學會寫 view 的表單

沒意外的話,明天就講怎麼查天氣。

沒有留言: