我打开信封,有张明信片在里面。明信片封面是一个大大的 Heroku 标志,背面则写着:亲爱的小艾,伟大的学者路德维希·希洛库曾经这麽说过,当眼睛看到美丽的 APP 时,手就会想在键盘上为她做一个 Deploy to Heroku 按钮。我多麽希望我也能为你做出一个按钮。
~节录自《聊天机器人的历史:如果不会,那就》
延伸自系列文 《从LINE BOT到资料视觉化:赖田捕手》
《赖田捕手:追加篇》:
今天您将知道:
第 34 天当中,我们大致了解了 LINE Notify 的运作方式,以及程序码的实作细节。那麽今天就来介绍一下我们可以怎麽做,来达到将使用者与 LINE Notify 建立连结,并且透过 LINE Notify 发送讯息给指定使用者的这个功能。
要让使用者与 LINE Notify 建立连结,我们首先要传送一个授权网址 (Auth Link) 给使用者,而要产生这样子的授权网址,我们需要 Client ID、Redirect URI、以及一个我们自行定义的 State,如图一。
图一、LINE BOT 产生授权网址
可以自行定义的 State 非常重要,因为之後 LINE Notify 给出来的连结权杖 (Access Token) 并没有关於使用者的任何资料,为了要知道哪一个连结权杖会对应到哪一个使用者,我们必须要用 State 来记录。因此这边就用 LINE 给出来的使用者代码来当作 State:
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}'
这边的user_id
可以从event.source.user_id
拿到,而client_id
和redirect_uri
则是我们在 LINE Notify 上登录服务的时候会拿到/使用的资料。至於 LINE BOT 该在什麽时候将这个授权网址发送给使用者,大家就可以自行设计。以我们目前想要做的双色打光来说,如果要将双色打光处理过後的图片都由 LINE Notify 发送给使用者的话,那这个网址就必须放在使用者容易拿到的地方,或是每一次互动都发送这个网址,或是更贴心一点,只发送给没有与 LINE Notify 建立连结的使用者。
当使用者进入授权网址,点击同意并连结之後,我们的 LINE BOT 要能收到 LINE Notify 产生的 Code,以及授权网址本身的 State,如图二。
图二、LINE USER 同意并连结
这件事我们之前已经示范过该如何实作了,就不罗嗦,程序码如下:
app/routes.py
from flask import request
@app.route("/callback/notify", methods=['GET'])
def callback_notify():
assert request.headers['referer'] == 'https://notify-bot.line.me/'
code = request.args.get('code')
state = request.args.get('state')
# 接下来要实作的程序码
success = handle_subscribe(code, state)
return '恭喜完成 LINE Notify 连动!请关闭此视窗。'
state = request.args.get('state')
state
,由於我们刻意安排的结果,其实就是使用者代码 (user_id
)。我们的 LINE BOT 拿到这个 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']
图三、LINE BOT 取得连结权杖
我们得好好地把这个连结权杖储存起来。说道储存资料,第一个想法当然就是 Heroku Postgress!我们的 Heroku Postgres 已经有一个表格user_dualtone_settings
用来储存使用者的双色打光设定,现在我们可以再建立一个表格,用来储存连结权杖 (当然,若大家想用相同的表格来储存这些资料也完全 OK,毕竟都可以用user_id
来当作表格的 Primary Key)。那麽,这个表格要储存哪些资料呢:
app/custom_models/CallDatabase.py
def init_table():
conn, cursor = access_database()
postgres_table_query = "SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname != 'pg_catalog' AND schemaname != 'information_schema'"
cursor.execute(postgres_table_query)
table_records = cursor.fetchall()
table_records = [i[0] for i in table_records]
print('CallDatabase: table_records', table_records)
# 用来储存连结权杖的表格
if 'notify_subscription' not in table_records:
create_table_query = """CREATE TABLE notify_subscription (
user_id VARCHAR ( 50 ) PRIMARY KEY,
access_token VARCHAR ( 50 ) NOT NULL,
subscribe BOOLEAN NOT NULL
);"""
cursor.execute(create_table_query)
conn.commit()
return True
关於user_dualtone_settings
的相关程序码之前介绍过了,这边我们就略过不看。我们将用来储存连结权杖的表格命名为notify_subscription
,里面需要放入这些资料:
CREATE TABLE notify_subscription (
user_id VARCHAR ( 50 ) PRIMARY KEY,
access_token VARCHAR ( 50 ) NOT NULL,
subscribe BOOLEAN NOT NULL
);
第一行:user_id VARCHAR ( 50 ) PRIMARY KEY,
我们用user_id
当作 Primary Key。
第二行:access_token VARCHAR ( 50 ) NOT NULL,
并且存入access_token
连结权杖。
第三行:subscribe BOOLEAN NOT NULL
最後一个则是这个连结是否仍然有效的布林值。若仍然有效,布林值为真。若已经失效,布林值为假。为什麽会有有效跟无效的差别呢?因为使用者随时可以解除 LINE Notify 的连结。这个时候我们的 LINE BOT 并不会收到任何通知,只有在尝试用授权连结发送讯息给使用者的时候,发送失败,才会知道原来使用者已经断开连结了。因此可以用一个栏位来追踪连结是否有效。
有了这个表格,当 LINE BOT 从 LINE Notify 拿到连结权杖之後,就可以存进来了 (图四):
app/custom_models/CallDatabase.py
def notify_subscribe(user_id, access_token, subscribe):
conn, cursor = access_database()
if notify_get_token(user_id):
postgres_delete_query = "DELETE from notify_subscription where user_id = %s"
cursor.execute(postgres_delete_query, (user_id, ))
conn.commit()
table_columns = '(user_id, access_token, subscribe)'
postgres_insert_query = f"INSERT INTO notify_subscription {table_columns} VALUES (%s,%s,%s)"
record = (user_id, access_token, subscribe)
cursor.execute(postgres_insert_query, record)
conn.commit()
cursor.close()
conn.close()
return record
回头看看我们在app/routes.py
当中,callback_notify
这个函式里答应要实作的handle_subscribe
:
def handle_subscribe(code, user_id):
access_token = get_token(code)
_ = CallDatabase.notify_subscribe(user_id, access_token, True)
return True
图四、Heroku Postgres 存入连结权杖
有没有觉得整个流程都配合起来的感觉了呢?
最後,当我们需要透过 LINE Notify 发送讯息给使用者的时候 (图五):
def send_message(user_id, access_token, im_url):
url = 'https://notify-api.line.me/api/notify'
headers = {"Authorization": "Bearer "+ access_token}
data = {
'message': '✌您点的双色打光已送达!',
'imageThumbnail': im_url,
'imageFullsize': im_url
}
data = urllib.parse.urlencode(data).encode()
req = urllib.request.Request(url, data=data, headers=headers)
try:
page = urllib.request.urlopen(req).read()
print('Notify Success!')
except:
_ = CallDatabase.notify_subscribe(user_id, access_token, False)
print('Notify Fail. User Unsubscribe.')
图五、Heroku Postgres 读取连结权杖
这麽一来,我们的 LINE BOT 就可以透过 LINE Notify 在需要的时候主动推送讯息给指定使用者,而不需要担心预算爆表的问题了!
经过扎实的五个星期,我们的双色打光 LINE BOT 终於完工了,草泥马们一定很开心,可以看着最爱的双色打光图片提振精神了。
但也许不是每位草泥马饲育家都像我们一样也身兼 LINE BOT 设计师。毕竟饲养草泥马本身就是一件艰困且耗时的工作,要是再要求每一位饲育家都能够略懂 Python,且了解该怎麽实作 LINE BOT,似乎有点太苛刻了。怎麽办呢?难道那些饲育家的草泥马,就永远跟双色打光的图片无缘了吗?
答案是否定的。
感谢 Heroku 推出的一个很棒的功能:【Deploy to Heroku 按钮】,让我们可以把自己设计出来的 APP 很快地分享给有兴趣的人,让大家不用重复相同的工作,每一次都重新开始。只要把自己的专案放到 Github 上,再加上一个 Deploy to Heroku 按钮,那麽下次想要写出一模一样的内容,只要按下这个按钮,不用写任何一行程序码,你也可以拥有双色打光 LINE BOT。是不是也很想知道该怎麽做出这个按钮呢?那麽我们现在就来看看吧!
首先要把我们用来架构 Heroku APP 的专案都搬到 Github 上。举例来说,以下是我们至今为止建立起的所有档案内容:
D:\appendix>tree /f
Folder PATH listing
Volume serial number is 9C33-6XXD
D:.
│ runtime.txt
│ requirements.txt
│ Procfile
│ Alma.py
│
└───app
│ __init__.py
│ models_for_line.py
│ routes.py
│
└───custom_models
AlmaTalks.py
AlmaRenders.py
AlmaNotify.py
CallDatabase.py
我们要把这些档案内容,按照该有的档案架构,上传到我们的 Github 当中独立的资源库 (repository) 里。也就是在 Github 当中建立一个参考的资源库。完成以後,只要在该资源库的主目录底下,增加一个app.json
档案,就算是做好准备工作了。这个app.json
有点像是配置档,根据该档案的内容,Deploy to Heroku 按钮才知道布署新的 APP 时,该注意或该添加哪些扩充元件。那该怎麽写这个app.json
呢:
{
"name": "你-APP-的专案名称",
"description": "你-APP-的专案描述",
"repository": "你-APP-的资源库网址",
"env": {
"CHANNEL_ACCESS_TOKEN": "your channel access token",
"CHANNEL_SECRET": "your channel secret",
"YOUR_HEROKU_APP_NAME": "your Heroku App name",
"NOTIFY_CLIENT_ID": "your Notify client ID",
"NOTIFY_CLIENT_SECRET": "your Notify client secret"
},
"addons": ["heroku-postgresql:hobby-dev"],
"success_url": "/"
}
第二行:"name": "你-APP-的专案名称",
我们 APP 的专案的名称,最多 30 字。可填可不填。
第三行:"description": "你-APP-的专案描述",
用来详细解释我们这个 APP 的用途,或是任何你想填入的话。可填可不填。
第四行:"repository": "你-APP-的资源库网址",
注记我们 APP 的专案是放在哪一个资源库当中。可填可不填。
第五行:"env": {
这个就重要了。这代表建立我们的 APP,需要用到的环境变数。记得把所有要用到的环境变数名称放进来,这样可以简化整个布署的流程。以我们目前的双色打光 LINE BOT 来说,需要用到的环境变数就包括用来验证 LINE BOT 身分的CHANNEL_ACCESS_TOKEN
、CHANNEL_SECRET
,用来验证 LINE Notify 身分的NOTIFY_CLIENT_ID
、NOTIFY_CLIENT_SECRET
,以及方便我们路由做处理的YOUR_HEROKU_APP_NAME
。
第十二行:"addons": ["heroku-postgresql:hobby-dev"],
这个也很重要,表示我们这个 APP 需要用到哪些扩充元件。记得把所有要用到的扩充元件都放进来,方法是用阵列 (Array) 的方式把所有需要的扩充元件列下来。以我们目前的双色打光 LINE BOT 来说,只需要用到 Heroku Postgres,那阵列当中就放进这个元素,并且用:hooby-dev
表示我们要用的方案名称。
第十三行:"success_url": "/"
当透过 Deploy to Heroku 按钮成功发布这个 APP 之後,会被重新导向的网址。可填可不填。
有没有发现很多都是可填可不填呢?不过记得要把env
和addons
这两个项目写对。关於app.json
更详细的说明,可以参考 Heroku 的官方文件。
都准备好了以後,就可以在存放我们 APP 详细内容的 Github 资源库当中,在README.md
这个说明文件里添加 Deploy to Heroku 按钮了。使用方式如下:
README.md
# 详细表示法
[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/你-Github-帐号/你-Github-资源库)
# 简易表示法
[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy)
详细表示法跟简易表示法的差别,在於详细表示法多了?template=https://github.com/你-Github-帐号/你-Github-资源库
这个参数,因此这样子的按钮可以在 Github 资源库的README.md
以外的地方使用。而简易表示法,因为没有特别指明是参照哪一个资源库,所以只能在 Github 资源库的README.md
里面使用。其他更详细的资料,可以参考 Heroku 的官方文件。
因为我们这个 APP 跟 LINE BOT 以及 LINE Notify 有关,要真的能使用这个 APP,请一起准备好 LINE BOT 的CHANNEL_ACCESS_TOKEN
、CHANNEL_SECRET
,以及 LINE Notify 服务的NOTIFY_CLIENT_ID
、NOTIFY_CLIENT_SECRET
。最後,当然,你要有一个 Heroku 帐号。都准备好了以後,就让我们来试用看看吧!
点击 Deploy to Heroku 按钮之後,会带到Heroku 的产生新 APP 页面上,如图六:
图六、Create new APP
接着就看到我们在app.json
填写的环境变数,这边会引导使用者将环境变数一个一个填好,如图七:
图七、Config Vars
填完之後,按下 Deploy,就开始布署新 APP 了,如图八。
图八、Build APP
等一阵子之後,看到布署成功的画面,如图九。
图九、Deploy to Heroku
记得要改 LINE BOT 的 Webhook URL (图十),以及 LINE Notify 的 Callback URL (图十一)。
图十、Webhook URL
图十一、Callback URL
最後让我们一起试一试吧:
图十二、今晚我想来点双色打光
图十三、Hello from Alma!
耶,《赖田捕手:追加篇》的全部内容就到这边结束了,感谢大家的阅读。如果有兴趣,想要了解更多,比如说怎麽将相似的 LINE BOT 布署到 AWS 上,欢迎参考《LINE Bot by Python 全攻略》。非常感谢 iT 邦帮忙跟博硕文化,没有他们的帮忙,就不会有这本书。Happy Coding!
<<: 非线性回归-多项式回归 (polynomial regression in r)
>>: [Cmoney 菁英软件工程师战斗营] IOS APP 菜鸟开发笔记----第七届菁英软件工程师战斗营结训心得
TemplateSendMessage - ConfirmTemplate confirm_temp...
下单-买 Q:股票下单(买)失败,出现讯息:全额预收 A:问营业员,营业员说,可能该股票最近涨浮过大...
阵列Array 在程序设计中是非常常见的工具,当我们要建立多个相同型态的资料时,就会建立阵列,阵列的...
Visual Studio 是微软开发的整合开发环境(IDE),简称 VS。 VS 能开发的程序语言...
WhatsApp是世界上最多人使用的即时通讯软件,每月有20亿活跃用户,用户透过WhatsApp每天...