iT邦幫忙

2021 iThome 鐵人賽

DAY 10
0
Software Development

用 PHP 打造專屬於自己的 Telegram 聊天機器人吧!系列 第 10

【PHP Telegram Bot】Day10 - Long Polling、持續接收與發送訊息

昨天有講到一個叫做 getUpdates 的方法,但我們沒有填任何參數,今天要利用 offset 與 timeout 達成 Long Polling

Long Polling

先再執行一次昨天最後的程式,你會看到有個叫做 update_id 東西
https://ithelp.ithome.com.tw/upload/images/20210919/20132916U0Kmy0N4SV.png
把它填到 offset 的值,然後 timeout 先填 60,表示 60 秒以內 TG 伺服器要回應你
https://ithelp.ithome.com.tw/upload/images/20210919/20132916c5Wb4vDInx.png
填好之後按下執行,你會發現和剛剛拿到的東西一模一樣,這是因為它會從你設定的 update_id 開始取訊息

我們把剛剛填的值加一,再執行看看
https://ithelp.ithome.com.tw/upload/images/20210919/20132916XgIVr5NVMn.png
你會發現程式跑了整整一分鐘才跑完,而且啥都沒有,只有印出一個空陣列 Array()
https://ithelp.ithome.com.tw/upload/images/20210919/201329164fnHwmHwae.png
這是因為 540834396 就是最後一則訊息了,並沒有 540834397 這則訊息

再執行一次程式,這次執行之後私訊機器人一則訊息
https://ithelp.ithome.com.tw/upload/images/20210919/201329169o05isQCyE.png
你會發現發送訊息後,TG 伺服器立刻就回應了,而且程式也馬上跑完了
https://ithelp.ithome.com.tw/upload/images/20210919/20132916Yl6vSBHKjZ.png
這個就是 Long Polling

發出請求時,伺服器會一直到有新訊息時才會回應,或是到達時間限制(timeout)時回應空訊息

這樣不僅不用一直發送請求,而且有新訊息時也能立刻收到


我們再來試個東西,如果把 540834397 改回 540834396 看看會發生什麼事
https://ithelp.ithome.com.tw/upload/images/20210919/20132916c5Wb4vDInx.png
按下執行後,你會發現 540834396 不見了,只剩 540834397

這是因為 TG 伺服器會把小於 offset 的 update_id 都當作是你已經處理過的了,並且將這些訊息捨棄,所以你就沒辦法再次讀取了
https://ithelp.ithome.com.tw/upload/images/20210919/20132916zyRUCNSfG6.png


處理收到的訊息

我們要用到 processMessage 這個官方範例裡的一個函式,這個函式裡要填的是 message,但是要怎麼把它取出來呢?

首先第一步,把 print_r() 刪除,改成 $response = ,記得後面的括號要刪掉一個
https://ithelp.ithome.com.tw/upload/images/20210919/20132916MnZfAX6LFT.png
把它照著層數接在 $response 的後面,message 是字串,所以要用引號 ' 包起來
https://ithelp.ithome.com.tw/upload/images/20210919/20132916kXornvR58N.png
接好之後會像這樣
https://ithelp.ithome.com.tw/upload/images/20210919/20132916ZnMr7dHDbv.png
最後把 processMessage() 加上去,別忘了後面的小括號 ) 和分號 ;
https://ithelp.ithome.com.tw/upload/images/20210919/20132916yMuNFDcuA6.png
按下執行後你會發現它印出了一坨東西

{"chat_id":127355800,"reply_to_message_id":24,"text":"Cool","method":"sendMessage"}

這是因為它用了第三種發送訊息的方式,但現在程式並不是在 Webhook 模式下運作,所以就直接 echo 到終端機上了

我們只需要將第 139 行的 apiRequestWebhook 改成 apiRequest 就行了
https://ithelp.ithome.com.tw/upload/images/20210919/20132916mnLnurAr7U.png
改完再次執行程式,此時 TG 「叮咚」了一聲
https://ithelp.ithome.com.tw/upload/images/20210919/201329165u4UDHJ1QX.png
成功用官方的函式處理了訊息


處理每則訊息

其實不只是 $response[0] 裡面有 message$response[1], $response[2]... 也會有 message,至於會到數字多少,這取決於一次取到了多少則訊息

如果你發送了兩則訊息才執行一次程式,此時就會出現兩個 message,那要如何每個 message 都處理到呢,這時候就要用到 foreach 這個函式,它會一個個的把裡面的東西交給你,直到全部完成為止

來稍微修改一下剛剛的程式,把 $response 交給 foreach,中間的 $update 就是 $response[0], $response[1]...(你可以用 print_r($update); 印出來看看),然後把 $update['message'] 填進 processMessage() 函式中,記得加分號~
https://ithelp.ithome.com.tw/upload/images/20210919/20132916JBm4TrZEP0.png
這樣每則訊息就都會處理到了


讓程式持續運作

聰明的你應該已經發現了,不管執行幾次它都會重複處理訊息,因為它總是從 offset 開始處理

每次執行一次程式就要改一次 offset,這樣多麻煩,當然是要讓程式自己設定

首先要找出這很多則訊息的最後一則訊息,我們可以用 count($response) 知道總共有幾則訊息,count($response) - 1 表示最後一則(因為是從 0 開始數),我們先將這個值存進一個叫做 $latest 的變數裡
https://ithelp.ithome.com.tw/upload/images/20210919/20132916mT0RnqbF9E.png
然後把 $latest 填進 $response 的第一個框框裡,第二個框框填上 'update_id',update_id 是字串,所以要用引號 ' 包起來
https://ithelp.ithome.com.tw/upload/images/20210919/20132916ynGSBDJqaJ.png
這樣就取得了最後一則訊息的 update_id

我們是要把 offset 的值填下一則訊息的 update_id,這樣才不會一直重複讀取同一則訊息,這邊只要在後方多打個 + 1 就行了,之後把計算的結果存進 $update_id 變數裡
https://ithelp.ithome.com.tw/upload/images/20210919/20132916LQlG3hEXfI.png
還記得會有沒取到訊息的時候對吧,沒取到訊息時 $latest 的值會變成 -1,但是框框裡不能出現負數,這時就要加個 if 條件 $latest !== -1,有讀取到訊息時才把 update_id 取出來
https://ithelp.ithome.com.tw/upload/images/20210919/20132916HXA3FPbL1d.png
再來就是把 $update_id 填到 offset 的值裡,還有加上 while (true) {},讓程式不斷重複執行
https://ithelp.ithome.com.tw/upload/images/20210919/20132916C84LxYMmOh.png
最後的最後,在最前面加上一行 $update_id = 0;
https://ithelp.ithome.com.tw/upload/images/20210919/20132916kZv3I27CQS.png
這樣就大功告成啦

執行程式後,試著跟機器人說 Hi、Hello 吧

對了,如果想要停止程式的話,就對著終端機按下 Ctrl + C,程式就會停止了


結尾

今天的內容可能有點困難,看不太懂沒關係

如果沒辦法照著做出來的話,歡迎直接複製下方程式碼來玩玩:

$update_id = 0;
while (true) {
    $response = apiRequest("getUpdates", array('offset' => $update_id, 'timeout' => 60));
    foreach ($response as $update) {
        processMessage($update['message']);
    }
    $latest = count($response) - 1;
    if ($latest !== -1) {
        $update_id = $response[$latest]['update_id'] + 1;
    }
}

最後,再來考考你

如果少加了 $update_id = 0; 會發生什麼事?

在底下留言你的答案吧!


上一篇
【PHP Telegram Bot】Day09 - 用 PHP 主動接收和發送訊息吧!
下一篇
【PHP Telegram Bot】Day11 - Webhook 與 Web Hosting
系列文
用 PHP 打造專屬於自己的 Telegram 聊天機器人吧!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言