在第二天:認識卡米狗提到過,見人說人話,見鬼說鬼話功能是考慮到多個群組都教了相同的關鍵字時,卡米狗應該在每個群組做出不同的回應,這樣才不會被討厭,於是就加入了這樣的功能。當有人說「姆咪姆咪」時,卡米狗會先檢查這個群組有沒有人教過看到「姆咪姆咪」要回應,如果教過多次,就回應最後一次學過的內容,如果都沒學過,那麼就再檢查其他群組有沒有學過「姆咪姆咪」。
也就是說,學說話指令在儲存時,應該也要儲存是在哪個頻道學會的。
目前的學說話指令:
# 學說話
def learn(received_text)
#如果開頭不是 卡米狗學說話; 就跳出
return nil unless received_text[0..6] == '卡米狗學說話;'
received_text = received_text[7..-1]
semicolon_index = received_text.index(';')
# 找不到分號就跳出
return nil if semicolon_index.nil?
keyword = received_text[0..semicolon_index-1]
message = received_text[semicolon_index+1..-1]
KeywordMapping.create(keyword: keyword, message: message)
'好哦~好哦~'
end
應該要改成這樣:
# 學說話
def learn(channel_id, received_text)
...略
KeywordMapping.create(channel_id: channel_id, keyword: keyword, message: message)
...略
end
重點是多傳入一個參數 channel_id
,然後存入 KeywordMapping
。
在關鍵字回覆的部分,原本是:
# 關鍵字回覆
def keyword_reply(received_text)
KeywordMapping.where(keyword: received_text).last&.message
end
則是改為:
# 關鍵字回覆
def keyword_reply(channel_id, received_text)
message = KeywordMapping.where(channel_id: channel_id, keyword: received_text).last&.message
return message unless message.nil?
KeywordMapping.where(keyword: received_text).last&.message
end
多加了這兩行:
message = KeywordMapping.where(channel_id: channel_id, keyword: received_text).last&.message
return message unless message.nil?
這兩行的意思是,先找同一個頻道內教過的關鍵字,如果有找到的話就直接回傳。
如果你想要深入學習資料模型的查詢,官方也有提供中文版的說明文件,在這裡:Active Record 查詢。
要記得把參數也傳給剛剛改好的函數,原本的主程式是這樣:
def webhook
# 學說話
reply_text = learn(received_text)
# 關鍵字回覆
reply_text = keyword_reply(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
要改成:
def webhook
# 學說話
reply_text = learn(channel_id, received_text)
# 關鍵字回覆
reply_text = keyword_reply(channel_id, received_text) if reply_text.nil?
...略
end
這樣就作完了嗎!?
還沒呢,我們的 KeywordMapping
根本沒有 channel_id
欄位呀!
我們需要使用資料庫遷移的方式來對 KeywordMapping 新增欄位,首先要先建立一個資料庫遷移檔。
指令是 rails generate migration
加上註解:
rails generate migration add_channel_id_to_keyword_reply
D:\只要有心,人人都可以作卡米狗\ironman>rails generate migration add_channel_id_to_keyword_reply
invoke active_record
create db/migrate/20180114163555_add_channel_id_to_keyword_reply.rb
D:\只要有心,人人都可以作卡米狗\ironman>
生成了一個檔案在 db/migrate
裡面,我們要在這個資料庫遷移檔裡打一點字,這是他目前的樣子:
class AddChannelIdToKeywordReply < ActiveRecord::Migration[5.1]
def change
end
end
要加一個欄位的話,要這樣寫:
class AddChannelIdToKeywordReply < ActiveRecord::Migration[5.1]
def change
add_column :keyword_mappings, :channel_id, :string
end
end
在 add_column
後面第一個參數是資料表名稱
,第二個參數是要新增的欄位名稱
,以及第三個參數:要新增的欄位格式
。欄位格式的話,沒意外通常都會是 :string
。
這裡寫好之後存檔,就可以作資料庫遷移了。為什麼我知道是這樣寫呢?文件在這裡:Active Record 遷移
D:\只要有心,人人都可以作卡米狗\ironman>rails db:migrate
== 20180114163555 AddChannelIdToKeywordReply: migrating =======================
-- add_column(:keyword_mappings, :channel_id, :string)
-> 0.0012s
== 20180114163555 AddChannelIdToKeywordReply: migrated (0.0020s) ==============
D:\只要有心,人人都可以作卡米狗\ironman>
如果資料庫遷移檔沒打錯字的話,就會看到這個結果。
首先上傳程式碼,要養成開著 heroku logs -t
的習慣。
測了一下會發現:
2018-01-14T16:56:36.918562+00:00 app[web.1]: I, [2018-01-14T16:56:36.918392 #4] INFO -- : [2a0784f2-c2b7-46c1-818e-5e5dd799e64c] Started POST "/kamigo/webhook" for 203.104.146.154 at 2018-01-14 16:56:36 +0000
2018-01-14T16:56:36.920295+00:00 app[web.1]: I, [2018-01-14T16:56:36.920209 #4] INFO -- : [2a0784f2-c2b7-46c1-818e-5e5dd799e64c] Processing by KamigoController#webhook as */*
2018-01-14T16:56:36.920486+00:00 app[web.1]: I, [2018-01-14T16:56:36.920397 #4] INFO -- : [2a0784f2-c2b7-46c1-818e-5e5dd799e64c] Parameters: {"events"=>[{"type"=>"message", "replyToken"=>"bffeaf21d2b64743b3268bd177ebbaff", "source"=>{"userId"=>"Uc68d82df46b7899e7d716f396ae8e91a", "type"=>"user"}, "timestamp"=>1515948996430, "message"=>{"type"=>"text", "id"=>"7310568889858", "text"=>"A"}}], "kamigo"=>{"events"=>[{"type"=>"message", "replyToken"=>"bffeaf21d2b64743b3268bd177ebbaff", "source"=>{"userId"=>"Uc68d82df46b7899e7d716f396ae8e91a", "type"=>"user"}, "timestamp"=>1515948996430, "message"=>{"type"=>"text", "id"=>"7310568889858", "text"=>"A"}}]}}
2018-01-14T16:56:36.920998+00:00 app[web.1]: W, [2018-01-14T16:56:36.920917 #4] WARN -- : [2a0784f2-c2b7-46c1-818e-5e5dd799e64c] Can't verify CSRF token authenticity.
2018-01-14T16:56:36.925356+00:00 app[web.1]: D, [2018-01-14T16:56:36.925257 #4] DEBUG -- : [2a0784f2-c2b7-46c1-818e-5e5dd799e64c] KeywordMapping Load (1.6ms) SELECT "keyword_mappings".* FROM "keyword_mappings" WHERE "keyword_mappings"."channel_id" = $1 AND "keyword_mappings"."keyword" = $2 ORDER BY "keyword_mappings"."id" DESC LIMIT $3 [["channel_id", "Uc68d82df46b7899e7d716f396ae8e91a"], ["keyword", "A"], ["LIMIT", 1]]
2018-01-14T16:56:36.925763+00:00 app[web.1]: I, [2018-01-14T16:56:36.925658 #4] INFO -- : [2a0784f2-c2b7-46c1-818e-5e5dd799e64c] Completed 500 Internal Server Error in 5ms (ActiveRecord: 1.6ms)
2018-01-14T16:56:36.927283+00:00 app[web.1]: F, [2018-01-14T16:56:36.927195 #4] FATAL -- : [2a0784f2-c2b7-46c1-818e-5e5dd799e64c]
2018-01-14T16:56:36.927428+00:00 app[web.1]: F, [2018-01-14T16:56:36.927362 #4] FATAL -- : [2a0784f2-c2b7-46c1-818e-5e5dd799e64c] ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column keyword_mappings.channel_id does not exist
2018-01-14T16:56:36.927431+00:00 app[web.1]: LINE 1: ...keyword_mappings".* FROM "keyword_mappings" WHERE "keyword_m...
2018-01-14T16:56:36.927432+00:00 app[web.1]: ^
2018-01-14T16:56:36.927438+00:00 app[web.1]: : SELECT "keyword_mappings".* FROM "keyword_mappings" WHERE "keyword_mappings"."channel_id" = $1 AND "keyword_mappings"."keyword" = $2 ORDER BY "keyword_mappings"."id" DESC LIMIT $3):
2018-01-14T16:56:36.927567+00:00 app[web.1]: F, [2018-01-14T16:56:36.927496 #4] FATAL -- : [2a0784f2-c2b7-46c1-818e-5e5dd799e64c]
2018-01-14T16:56:36.927701+00:00 app[web.1]: F, [2018-01-14T16:56:36.927580 #4] FATAL -- : [2a0784f2-c2b7-46c1-818e-5e5dd799e64c] app/controllers/kamigo_controller.rb:82:in `keyword_reply'
2018-01-14T16:56:36.927702+00:00 app[web.1]: [2a0784f2-c2b7-46c1-818e-5e5dd799e64c] app/controllers/kamigo_controller.rb:10:in `webhook'
2018-01-14T16:56:36.929306+00:00 heroku[router]: at=info method=POST path="/kamigo/webhook" host=people-all-love-kamigo.herokuapp.com request_id=2a0784f2-c2b7-46c1-818e-5e5dd799e64c fwd="203.104.146.154" dyno=web.1 connect=0ms service=11ms status=500 bytes=1827 protocol=https
我先把前面那些多餘的字移除:
Started POST "/kamigo/webhook" for 203.104.146.154 at 2018-01-14 16:56:36 +0000
Processing by KamigoController#webhook as */*
Parameters: {"events"=>[{"type"=>"message", "replyToken"=>"bffeaf21d2b64743b3268bd177ebbaff", "source"=>{"userId"=>"Uc68d82df46b7899e7d716f396ae8e91a", "type"=>"user"}, "timestamp"=>1515948996430, "message"=>{"type"=>"text", "id"=>"7310568889858", "text"=>"A"}}], "kamigo"=>{"events"=>[{"type"=>"message", "replyToken"=>"bffeaf21d2b64743b3268bd177ebbaff", "source"=>{"userId"=>"Uc68d82df46b7899e7d716f396ae8e91a", "type"=>"user"}, "timestamp"=>1515948996430, "message"=>{"type"=>"text", "id"=>"7310568889858", "text"=>"A"}}]}}
Can't verify CSRF token authenticity.
KeywordMapping Load (1.6ms) SELECT "keyword_mappings".* FROM "keyword_mappings" WHERE "keyword_mappings"."channel_id" = $1 AND "keyword_mappings"."keyword" = $2 ORDER BY "keyword_mappings"."id" DESC LIMIT $3 [["channel_id", "Uc68d82df46b7899e7d716f396ae8e91a"], ["keyword", "A"], ["LIMIT", 1]]
Completed 500 Internal Server Error in 5ms (ActiveRecord: 1.6ms)
ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column keyword_mappings.channel_id does not exist
LINE 1: ...keyword_mappings".* FROM "keyword_mappings" WHERE "keyword_m...
^
SELECT "keyword_mappings".* FROM "keyword_mappings" WHERE "keyword_mappings"."channel_id" = $1 AND "keyword_mappings"."keyword" = $2 ORDER BY "keyword_mappings"."id" DESC LIMIT $3):
app/controllers/kamigo_controller.rb:82:in `keyword_reply'
app/controllers/kamigo_controller.rb:10:in `webhook'
at=info method=POST path="/kamigo/webhook" host=people-all-love-kamigo.herokuapp.com request_id=2a0784f2-c2b7-46c1-818e-5e5dd799e64c fwd="203.104.146.154" dyno=web.1 connect=0ms service=11ms status=500 bytes=1827 protocol=https
我們要關注的重點在:
Completed 500 Internal Server Error in 5ms (ActiveRecord: 1.6ms)
當你看到 500 Internal Server Error
,表示程式跑到一半就掛了,掛點原因通常會寫在這個訊息後面。
一個正常的 Log 是長這樣:
Completed 200 OK in 269ms (ActiveRecord: 9.9ms)
掛點原因:
ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column keyword_mappings.channel_id does not exist
LINE 1: ...keyword_mappings".* FROM "keyword_mappings" WHERE "keyword_m...
^
SELECT "keyword_mappings".* FROM "keyword_mappings" WHERE "keyword_mappings"."channel_id" = $1 AND "keyword_mappings"."keyword" = $2 ORDER BY "keyword_mappings"."id" DESC LIMIT $3):
app/controllers/kamigo_controller.rb:82:in `keyword_reply'
app/controllers/kamigo_controller.rb:10:in `webhook'
他說:ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column keyword_mappings.channel_id does not exist
,意思是 keyword_mappings
表格裡面沒有 channel_id
這個欄位。
這個叫做 exception message
。一般來說遇到絕大多數的問題都可以拿 exception message
去餵給 google ,就能得到問題的答案。不過看到這裡應該就能猜到是忘記作 Heroku 上的資料庫遷移了。
另外,最後面的那兩行:
app/controllers/kamigo_controller.rb:82:in `keyword_reply'
app/controllers/kamigo_controller.rb:10:in `webhook'
這個叫做 stack trace
。
意思是他死在 kamigo_controller.rb
的第 82
行,是在 keyword_reply
方法裡。而為什麼他會跑進這個方法呢?原來是在 kamigo_controller.rb
在 webhook
方法裡的第 10
行的呼叫了 keyword_reply
方法。
透過閱讀 stack trace
你通常就能夠找到錯誤的根源。
一如往常:
heroku run rake db:migrate
如果你明明已經跑了資料庫遷移程式,但他還是找不到新欄位的話,可以試試看重開 heroku server:
heroku restart
應該是順利啦~
想問一下,如果要向卡米狗那樣可以刪除一些關鍵字的話(卡米狗壞壞),該如何寫進程式裡?
你需要新增一個資料模型去紀錄上一次回應的關鍵字,然後遇到有人說[卡米狗壞壞]時,就去查這個表,找到應該要刪除哪一組學習紀錄。
刪除的指令是 KeywordMapping.where(...這裡就看你要用什麼方式查).destroy_all
不過我並沒有直接刪除,而是標記成封鎖。
不知道為甚麼做這個指令 log上的確有刪除訊息 關鍵字卻還是能叫出來
卡米大我想問一下就是如果我發一張圖片給卡米狗 取得image_id後
有辦法直接儲存然後轉送給其他人嗎(以關鍵字觸發)
{
"events"=>[
{
"type"=>"message",
"replyToken"=>"fb2915667ecf4589a7c512690f1d4b64",
"source"=>{
"userId"=>"Ud383eebf313db8b24dd8af72ae5a0b35",
"type"=>"user"
},
"timestamp"=>1516022386121,
"message"=>{
"type"=>"image",
"id"=>"0000000000000"
}
}
],
"kamigo"=>{
"events"=>[
{
"type"=>"message",
"replyToken"=>"fb2915667ecf4589a7c512690f1d4b64",
"source"=>{
"userId"=>"Ud383eebf313db8b24dd8af72ae5a0b35",
"type"=>"user"
},
"timestamp"=>1516022386121,
"message"=>{
"type"=>"image",
"id"=>"0000000000000"
}
}
]
}
}
這是 Line Messaging API 官方文件提供的圖片訊息格式:
{
"type": "image",
"originalContentUrl": "https://example.com/original.jpg",
"previewImageUrl": "https://example.com/preview.jpg"
}
你必須提供網址,但是你從 Line Webhook 獲得的圖片訊息不會含有網址,所以不行直接用。
你要把圖片下載下來之後再找個地方放圖,最後把網址傳出去。
你好 昨天的問題經過把資料夾改成英文真的解決了 謝謝
可是我在做新的步驟的時候又掛了... 我怕漏了什麼又把23~26天重做很多次
又變回一直被已讀的狀態 請問是哪裡出問題
kamigo_controller 第 107 行 你呼叫了 learn 方法
但你傳遞的參數數量跟你的方法定義的參數數量不同
你傳兩個參數,但你的方法定義只接收一個參數
在log訊息中可以看見這些資訊喔~
為甚麼他是以我最後一個打的回覆訊息回復?
剛剛隨便弄弄改改
就好了 哈哈
我也是出現相同問題
請問大大是怎麼解決的
今天試做時也遇到這問題
檢查了程式沒問題
上傳了2~3次程式+資料庫遷移
也重啟過資料庫都沒成功
heroku restart
最後多做了2次資料庫遷移
heroku run rake db:migrate
就莫名好了...
卡米大,我在 在 {KeywordMapping 資料模型中新增欄位}
這個部分出了錯誤了QQ。 我原先做到28天,因為錯誤所以回到27天重作,結果就壞惹
然後我現在遇到了奇怪的問題,localhost一直無法連線,上網爬了一些資料,也是過開記事本(host)刪除註解,防火牆也看過,結果還是這樣QAQQQ 想請問卡米大這種類似情形該怎麼辦??
執行網頁伺服器時的畫面:
應該是改到不該改的檔案了 但看不出來你改到什麼了耶
您好,我做到第25天,資料都可正常傳送,但是第26天上傳後,就沒有回應,用postmanz發現找不到網頁,又發現LINE Developers那邊的Webhook URL也是出現找不到的紅字,後來把主程式刪減到
def webhook
head :ok
end
這三行,重新verify,就success了。之後就重新把刪掉的再做上去,但是還是沒反應,麻煩幫我看看可以怎麼處理...謝謝您
重作前的log https://drive.google.com/open?id=12kq2ZurqTQJ0xOLv95rnuQrV0X07zNRc
重作後的log https://drive.google.com/open?id=1pCJgHknbEZftDrdco_nSHDx8IQSuFP5l
手殘,圖片還沒傳完就不小心按了留言,繼續傳
https://ithelp.ithome.com.tw/upload/images/20180914/20111702COcLbep98v.jpg
你在第 65 行用到一個變數叫做 keyword,但是你沒有告訴他 keyword 是什麼。
卡米大我想問個問題,如果我們把機器人分別
如果在自己的群組裡教機器人 A;0
在A群組裡教 A;1
那在B群組裡怎讓它顯示我們教的是A;0,而不是A;1呢?
(實際情況,他會顯示A;1)
謝謝
你要記錄自己的 user id ,然後優先用自己的 user id 查詢一次自己有沒有教過
好的,謝謝卡米大,我再自行摸索看看!