iT邦幫忙

0

《賴田捕手:追加篇》第 34 天:妥善運用 LINE Notify 免費推播

第 34 天:妥善運用 LINE Notify 免費推播

「恩...我是在想你今天晚上要不要用push_message來發送訊息。」
「不行。」
「怎麼了?」
「我已經有其他計畫了。」
「什麼計劃?」
「我要去看個電影。」
「跟誰?」我的胃不禁一陣翻攪,拜託不要是-
「LINE Notify,」他說。

~節錄自《聊天機器人的歷史:氾濫的訊息》

延伸自系列文 《從LINE BOT到資料視覺化:賴田捕手》

《賴田捕手:追加篇》:

今天您將知道:

  1. LINE Notify 運作流程
  2. LINE Notify 登錄服務
  3. LINE Notify 程式碼實作

上一篇我們將使用者設定的條件當作雙色打光的參數,完整的實作了一個可以客製化影像處理的 LINE BOT。我只能想得到兩個形容詞來形容這位 LINE BOT。第一個,是貼心。第二個,還是貼心。為什麼這樣說呢?因為這位 LINE BOT 不僅對使用者輸入的設定言聽計從,更有甚者,我們甚至還讓這位 LINE BOT 動用了push_message,為的就是能夠給使用者更完整的訊息,並且創造更多與使用者的互動。
然而 LINE BOT 的push_message是有使用次數限制的。在免費方案 (也就是輕用量) 的 LINE BOT,每個月擁有 500 次push_message的額度,如圖一。若要更多,就只能透過犧牲荷包,來召喚更多的push_message使用額度了。

https://ithelp.ithome.com.tw/upload/images/20210110/20120178w2fg0mN18D.png
圖一push_message方案

不想這麼做的話,難道就沒有其他方法了嗎?

幸好我們還有 LINE Notify。

什麼是 LINE Notify 呢?這是一種特殊的 LINE 官方帳號,可以讓使用者對某一個頻道做連結 (訂閱)。只要該頻道有任何訊息或是動作,就可以透過 LINE Notify 發送通知給使用者。在我們接下來要討論的內容裡,所謂的某一種頻道,當然就是 LINE BOT。
那麼重新整理一下思路再說一次。LINE Notify 是一種特殊的 LINE 官方帳號,讓使用者可以對 LINE BOT 做連結 (訂閱)。這麼一來,LINE BOT 就可以透過 LINE Notify 傳送相關訊息給使用者。
咦?這樣不是很怪嗎?LINE BOT 要對使用者傳訊息,還得透過 LINE Notify?
是的,沒錯,也就是讓 LINE Notify 當作傳話人的角色。雖然讓 LINE BOT 利用 LINE Notify 與使用者來溝通,可能會造成不同的使用者體驗,不過,這樣做有一個最大的優勢,那就是免費。不論是向使用者回覆訊息,向使用者推送訊息,都免費。這樣一來,大家想把訊息推送的多完整、多即時、多惱人,都可以透過 LINE Notify 來達成願望。
好啦,說了這麼多好話,還是得跟各位澄清一下 LINE Notify 的幾個不足之處:

  1. 必須和使用者建立連結 (訂閱)
  2. LINE Notify 只能發送文字、有限的 LINE 官方貼圖、以及圖片。而且每一次推送基本都必須發送一則文字訊息。

那麼,就開始來介紹 LINE Notify 的運作流程囉!

LINE Notify 運作流程

這邊我試著以圖二來描述 LINE Notify 與使用者以及 LINE BOT 之間的互動流程。

https://ithelp.ithome.com.tw/upload/images/20210110/20120178FLfm5hGb0D.png
圖二、LINE USER ⇄ LINE Notify ⇄ LINE BOT

首先是第一階段,也就是產生授權網址 (Auth Link)。為了要有授權網址,我們要先在 LINE Notify 上面登錄一個服務,拿到 Client ID、Redirect URI 這兩個資料,而 State 是我們可以自訂的資料。根據 Client ID、Redirect URI 和 State,我們可以產生一個相對應的授權連結,透過 LINE BOT 發送給使用者。
到了第二階段,使用者看到了我們的授權網址,點擊下去同意建立連結 (訂閱) 之後,會產生一組 Code,並傳送到 Redirect URI 這個地方。方便起見,我們當然是將 Redirect URI 指向 LINE BOT,讓 LINE BOT 直接接收這一組 Code。
第三階段,拿到代表使用者同意建立連結的 Code 之後,LINE BOT 就可以向 LINE Notify 申請 Access Token。這一個步驟,LINE BOT 需要拿出包括 Client ID、Client Secret、Redirect URI、和 Code 等資料。確認無誤之後,LINE Notify 就會傳回 Access Token。
第四階段,有了 Access Token 的 LINE BOT,就可以利用這一組權杖當作媒介,透過 LINE Notify 發送訊息給特定的使用者了。

LINE Notify 登錄服務

大致的流程就如上所述。為了要讓整個流程得以展開,首先我們得在 LINE Notify 上面登錄一個服務。先來到 LINE Notify 的官方網站,右上角以 LINE 帳號登入。登入之後,選擇管理登錄服務,並點選登錄服務。如此會來到一個建立新服務的畫面,如圖三

https://ithelp.ithome.com.tw/upload/images/20210111/20120178CUIIvazesv.png
圖三、登錄服務

裡面最重要的有三個欄位:服務名稱、電子郵件信箱、以及 Callback URL。因為所有和 LINE Notify 連動的服務,都是透過 LINE Notify 發送訊息給使用者。那麼使用者要怎麼知道這次送來的訊息是哪一個服務呢?靠的就是這個服務名稱。所以服務名稱請寫的簡單明瞭,讓使用者一看就知道這是代表我們的 LINE BOT 送來的訊息。電子郵件信箱則是啟用 LINE Notify 服務時,需要點擊連結進行認證用的,選一個你收得到信的電子郵件信箱就可以。最後一個 Callback URL 想當然爾就是前面提過的 Redirect URI。以我目前想要為雙色打光 LINE BOT 搭配的 LINE Notify 服務而言,我所填寫的欄位如圖四所示。注意這邊我所填寫的 Redirect URI 是https://你-APP-的名字.herokuapp.com/callback/notify,這是我們之後要準備接收 LINE Notify 相關資料的路由,待會必須要實作出來。

https://ithelp.ithome.com.tw/upload/images/20210111/20120178zPoLf4tTf5.png
圖四、雙色打光 feat. LINE Notify

確認無誤之後,按下登錄並完成電子郵件認證,這樣一來新的 LINE Notify 服務就成功建立了。此時我們查看這個 LINE Notify 服務,應該可以看到 Client ID 和 Client Secret 這兩個資料,如圖五

https://ithelp.ithome.com.tw/upload/images/20210111/20120178TgDbF4d23p.png
圖五、代表我們 LINE Notify 服務的 Client ID 和 Client Secret

這樣就完成登錄服務的部分了!

LINE Notify 程式碼實作

註:第二階段的詳細實作已經於 2021.01.11 更新

接下來進入大家最感興趣的部份了,也就是該怎麼把圖二的所有流程用程式碼實作出來。為了方便閱讀,我們把圖二再看一次,如圖六

https://ithelp.ithome.com.tw/upload/images/20210111/20120178FptkFAigI9.png
圖六、LINE USER ⇄ LINE Notify ⇄ LINE BOT,again

我們分階段來演示。首先是第一階段,產生授權網址:

import os, urllib

client_id = os.environ['NOTIFY_CLIENT_ID']
client_secret = os.environ['NOTIFY_CLIENT_SECRET']
redirect_uri = f"https://{os.environ['YOUR_HEROKU_APP_NAME']}.herokuapp.com/callback/notify"

def create_auth_link(user_id, client_id=client_id, redirect_uri=redirect_uri):
    
    data = {
        'response_type': 'code', 
        'client_id': client_id, 
        'redirect_uri': redirect_uri, 
        'scope': 'notify', 
        'state': user_id
    }
    query_str = urllib.parse.urlencode(data)
    
    return f'https://notify-bot.line.me/oauth/authorize?{query_str}'
  • 第二行:client_id = os.environ['NOTIFY_CLIENT_ID']
    因為這些程式碼最後都會成為我們 LINE BOT 的一部分,放到 Heroku 上面,所以可以把client_idclient_secret這些資料利用環境變數的方式存到 Heroku 裡面。

  • 第四行:redirect_uri = f"https://{os.environ['YOUR_HEROKU_APP_NAME']}.herokuapp.com/callback/notify"
    這是我們之後要準備接收 LINE Notify 相關資料的路由,待會必須要實作出來。

  • 第六行→第十二行:data = {
    前面說過,要產生授權網址,我們需要準備好 Client ID、Redirect URI、以及 State。在這裡面,State 是可以任意輸入的資料,也是讓我們可以藉由 LINE Notify 推送訊息到指定使用者最重要的手段。怎麼說呢?因為最後我們將會拿到的 Access Token 其實沒有任何跟使用者相關的資料,單看 Access Token 是沒辦法知道 LINE Notify 會把訊息傳給誰的,所以一開始我們就要記好。可是要怎麼記住呢?這個任意輸入的 State 就是一個非常方便的變數。我們可以將 LINE 的使用者代碼 (user_id) 放到 State 裡面,如此一來,這一個授權網址就代表一個使用者,而根據該授權網址所產生的 Access Token,就可以連結到這個特定的使用者身上了。

  • 第十四行:return f'https://notify-bot.line.me/oauth/authorize?{query_str}'
    根據給定的資料產生授權網址,如此完成第一階段。

第二階段從使用者進入我們給予的網址開始,到 LINE BOT 拿到 Code 和 State 為止。使用者點擊授權網址之後,應該會帶到如圖七所示的網頁上:

https://ithelp.ithome.com.tw/upload/images/20210111/20120178q3AuZ9h3dm.png
圖七、授權網址

一般來說會選擇連動到**「透過 1 對 1 聊天接收 LINE Notify 通知」**。當使用者選擇好接收通知的聊天室,同意並連動之後, LINE Notify 會將 Code 和 State 等資料送到 Redirect URI。為了方便起見,我們希望這些資料可以被送到 LINE BOT 手上,因此把 Redirect URI 設定成https://你-APP-的名字.herokuapp.com/callback/notify
現在,讓我們試著用flask來實作這一個路由,並且藉由解析查詢字串 (query string) 來拿到使用者同意並連動之後產生的 Code 跟其實是使用者代碼 (user_id) 的 State。

  • app/routes.py
from flask import request

@app.route("/callback/notify", methods=['GET'])
def callback_nofity():
    assert request.headers['referer'] == 'https://notify-bot.line.me/'
    code = request.args.get('code')
    state = request.args.get('state')

    # 接下來要繼續實作的函式
    access_token = get_token(code, client_id, client_secret, redirect_uri)

    return '恭喜完成 LINE Notify 連動!請關閉此視窗。'
  • 第二行:@app.route("/callback/notify", methods=['GET'])
    這就是我們要建立的路由,https://你-APP-的名字.herokuapp.com/callback/notify

  • 第四行:assert request.headers['referer'] == 'https://notify-bot.line.me/'
    先確定一下這是從 LINE Notify 發送過來的資料。

  • 第五行→第六行:code = request.args.get('code')
    LINE Notify 是用查詢字串的方式將資料送過來的。所以如果大家有機會看到 LINE Notify 向我們 Redirect URI 請求的網址的話,看起來應該會像這樣:https://你-APP-的名字.herokuapp.com/callback/notify?code=一串Code&state=一串State。要拿到查詢字串裡面的資料也不困難,用flask所提供的方法request.args.get就可以了。

  • 第八行:access_token = get_token(code, client_id, client_secret, redirect_uri)
    有了 Code 之後,就可以到第三階段去向 LINE Notify 請求 Access Token 了。這是等等要來實作的函式。

  • 第九行:return '恭喜完成 LINE Notify 連動!請關閉此視窗。'
    這個則是使用者同意並連動之後,最後會看到的內容。所以才叫 Redirect URI,也就是使用者同意並連動之後,會被重新導向的網址。當然大家可以把這個網頁做得漂亮一點,不過我們這邊簡單起見,就用一行字恭喜使用者完成訂閱,如圖八

https://ithelp.ithome.com.tw/upload/images/20210111/20120178p8Hh0ig4UE.png
圖八、恭喜您完成連動!

第三階段,利用使用者同意並連動之後產生的 Code,來向 LINE Notify 拿取最重要的 Access Token:

import json

def get_token(code, client_id=client_id, client_secret=client_secret, redirect_uri=redirect_uri):
    url = 'https://notify-bot.line.me/oauth/token'
    headers = { 'Content-Type': 'application/x-www-form-urlencoded' }
    data = {
        'grant_type': 'authorization_code',
        'code': code,
        'redirect_uri': redirect_uri,
        'client_id': client_id,
        'client_secret': client_secret
    }
    data = urllib.parse.urlencode(data).encode()
    req = urllib.request.Request(url, data=data, headers=headers)
    page = urllib.request.urlopen(req).read()
    
    res = json.loads(page.decode('utf-8'))
    return res['access_token']
  • 第十二行:data = urllib.parse.urlencode(data).encode()
    準備好需要的資料,包括client_idclient_secretredirect_uricode

  • 第十四行:page = urllib.request.urlopen(req).read()
    傳送資料到 LINE Notify,並讀取 LINE Notify 回傳的資料。

  • 第十六行:return res['access_token']
    回傳的資料可以利用json.loads轉換為字典物件 (dict)。該物件當中有一個鍵 (key) 為access_token的資料,就示我們朝思暮想的 Access Token。

拿到 Access Token 的時候,我們可以利用 Heroku 提供的資料庫把 Access Token 和對應的使用者代碼 (user_id) 給儲存起來。這邊我就不再詳述該如何實作,大家可以參考前面的內容 (第 17 天第 32 天) 自由發揮。完成之後,我們就可以進入第四階段,開始肆無忌憚的騷擾使用者了:

def send_message(access_token, text_message, picurl):

    url = 'https://notify-api.line.me/api/notify'
    headers = {"Authorization": "Bearer "+ access_token}

    data = {'message': text_message, 
            "stickerPackageId": 2, 'stickerId': 38, 
            'imageThumbnail':picurl, 'imageFullsize':picurl}

    data = urllib.parse.urlencode(data).encode()
    req = urllib.request.Request(url, data=data, headers=headers)
    page = urllib.request.urlopen(req).read()
  • 第四行:data = {'message': text_message,
    代表要向使用者發送的文字訊息text_message。在 LINE Notify 的規則裡,每次要發送訊息,都必須帶有一則文字訊息,所以這個欄位是必須的。

  • 第五行:"stickerPackageId": 2, 'stickerId': 38,
    發送貼圖的方法。上面這行程式碼代表發送第 2 組官方貼圖組當中的第 38 個貼圖。

  • 第六行:'imageThumbnail':picurl, 'imageFullsize':picurl}
    發送圖片的方法。一樣必須要用網址的方式傳送圖片資料。

今天實在是非常的匆忙,如果有任何介紹得不夠清楚的,都歡迎大家在這邊留言討論。有興趣的讀者們相信已經可以開始磨刀霍霍準備要動手實作一個結合 LINE Notify 的 LINE BOT 了。我個人詳細的內容則會在下一週仔細介紹。

好的,相信大家知道接下來又要進入最重要的工商時間了。是的,即使在一陣兵荒馬亂當中,還是要感謝 iT邦幫忙 和 博碩文化,LINE Bot by Python 全攻略 集結成書了,歡迎有興趣的大家前往購書喔。


1 則留言

0
mobo
iT邦新手 4 級 ‧ 2021-01-25 17:17:33

請問大大,
我把line notify 跟line Bot 放在同一聊天群組裡,我自己發訊息 line Bot會回應,但是 Line Notify 發的訊息line Bot不會回應。 請問有方法可以解嗎? 讓
Line Notify 發的訊息,line Bot 回應
感謝

我要留言

立即登入留言