###### tags: `學業/工作`
# 自製教學文區(linebot)
### line github heroku 操作流程
#### 註冊line bot api
切成中文->右上角log in可以用自己的line或創新帳號->創建providers(名字沒差)->選右上角Create a Messaging API channel->channel icon可以上傳機器人的頭貼->channel name輸入機器人的名字->
basic的secret->description可以輸入介紹->下面兩欄隨便選->底下兩個打勾條款打勾後create->到Messaging API將Use webhook選項enable->底下Auto-reply message與Greeting messages選項disable
#### 註冊heroku
搜尋heroku到官網->右上角sign up->註冊時語言選python
#### 註冊github
搜尋github到官網->右上角sign up->註冊完右上角 New repository->輸入名稱選私人->底下create repository->uploading an existing file->上傳此網址下載的檔案(要解壓縮並分別上傳)
https://github.com/NCULineBot/echoBotExample

#### 串接heroku、github
打開READMD.txt把github帳號名稱與repository名稱替換掉
commit後點開按鈕->輸入一個不會重複的名稱->回LINE Developers機器人頁面的basic setting取得Channel secret->Messaging API取得Channel access token->create app
#### 串接line API、heroku
部屬完成後點右上角open app->複製網址列網址->貼回line API的Webhook URL->update->verify->複製上面的Bot basic ID->使用手機line加入好友就完成了
### goole sheet API 註冊流程
搜尋:Google API console->登陸google帳號
->選取專案->新增專案->填寫名稱後建立
->搜尋google sheet->啟用
->右上角建立憑證->存取應用程式資料->不會使用任何一項憑證
->輸入服務帳戶名稱後繼續->點選完成->
->點選底下的服務帳戶信箱->點選金鑰->新增金鑰->建立新的金鑰->類型選json
->下載後將它放入pyhon檔案資料夾
->創建新的google試算表->共用->貼入剛剛的服務帳戶信箱並選編輯者->共用
### 套件使用
import gspread
from oauth2client.service_account import ServiceAccountCredentials as SAC
Json = '這裡放剛剛下載的json檔'
url = ["https://spreadsheets.google.com/feeds"]
Connect = SAC.from_json_keyfile_name(Json, Url)
GoogleSheets = gspread.authorize(Connect)
Sheet = GoogleSheets.open_by_key('這裡放試算表代號')
Sheets = Sheet.sheet1
### 基礎語法說明(google sheet API)
```python=
Sheets.clear()#清空試算表
data1 = [1, 2, 3, 4, 5]
data2 = [6, 7, 8, 9, 10]
data3 = [11, 12, 13, 14, 15]
Sheets.append_row(data1)
Sheets.append_row(data2)
Sheets.append_row(data3)
```

```python=
datas = Sheets.get_all_values()
print(type(datas))
print(datas)
```
```
<class 'list'>
[[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15]]
```
### line_bot_api語法說明
我們一開始要在這個區塊內改動看看:
```python
@handler.add(MessageEvent, message=TextMessage)#只接受類別為text的訊息
def handle_message(event):
msg = event.message.text #將訊息存在msg字串內
print(msg)
#將msg包入TextSendMessage(官方指定回傳格式),再使用reply_message這個方法進行回傳
line_bot_api.reply_message(event.reply_token,TextSendMessage(text=msg))
```
有時使用者傳來的不一定是文字訊息~
```python
@handler.add(MessageEvent)
def handle_message(event):
if event.message.type == "text":
msg = event.message.text
print(msg)
line_bot_api.reply_message(event.reply_token,TextSendMessage(text=msg))
elif event.message.type == "sticker":
#將使用者傳送的貼圖資料包進新的貼圖物件再回傳
sticker = StickerSendMessage(package_id=f"{event.message.package_id}", sticker_id=f"{event.message.sticker_id}")
line_bot_api.reply_message(event.reply_token, sticker)
elif event.message.type == "sticker"ImageSendMessage(original_content_url=uploaded_image.link, preview_image_url=uploaded_image.link)
else:
line_bot_api.reply_message(event.reply_token,TextSendMessage(text=f"我知道你傳了{event.message.type}給我~"))
```
>也可以來這裡找喜歡的貼圖
https://developers.line.biz/en/docs/messaging-api/sticker-list/#sticker-definitions
:::spoiler 如果想要回傳多個訊息物件怎麼辦?
> Ans:使用陣列
:::
```python=
msg = [TextSendMessage(text="這是第{i}次了╰(‵□′)╯") for i in range(5)]
line_bot_api.reply_message(event.reply_token, msgs)
```
:::warning
reply_message最多傳送5個訊息喔~
:::
#### 建立選單
如果所有溝通都要使用文字好像有點麻煩...
要怎麼做出酷酷的選單

首先我們要先建立一個選單物件
```python=
myLabel = TemplateSendMessage(
alt_text='',#預覽訊息
template=ButtonsTemplate(
title='',#選單標題
text='',#選單描述
#按下不同的按鈕會觸發不同的action
actions=[
PostbackAction(
label='',#選項標題
display_text='',#按下按鈕使用者會說的話
data='myLabel_postback1'#回傳後台的訊息
),
PostbackAction(
label='',
display_text='',
data='myLabel_postback2'
),
#若建立URIAction可以放網址
URIAction(
label='google看看好了',
uri="https://www.google.com.tw/")
]
)
)
line_bot_api.reply_message(event.reply_token, myLabel)
```
:::warning
action陣列最多接受4個選項!
只有在手機上才能顯示TemplateSendMessage喔~
:::
接著我們要創建新的handler接住剛剛模板訊息的回傳
```python=
@handler.add(PostbackEvent)
def Postback(event):
get_postback_data = event.postback.data
if get_postback_data == "myLabel_postback1":
line_bot_api.reply_message(event.reply_token, TextSendMessage(text="收到來自按鈕一的訊息"))
elif get_postback_data == "myLabel_postback2":
line_bot_api.reply_message(event.reply_token, TextSendMessage(text="收到來自按鈕二的訊息"))
```
這樣就完成選單的製作了~
確認選單也是同理:
```python=
mypicker = TemplateSendMessage(
alt_text='',
template=ConfirmTemplate( #只有這裡改成ConfirmTemplate而已
text='',
title='',
actions=[
PostbackAction(
label='是',
display_text='是',
data='true'
),
PostbackAction(
label='否',
display_text='否',
data='false'
)#一樣要在Postback函式做對應的處理喔~
]
)
)
line_bot_api.reply_message(event.reply_token, mypicker)
```
接著是日期選單物件:
```python=
import time #使用python內建的time套件
ini_y, ini_m, ini_d = '2022', '01', '01'
# 因為可能多次初始化時間便把它寫成函式
def get_now_time():
#將變數宣告為global才能從函式內部改動外面的變數
global ini_y, ini_m, ini_d
now_time = time.localtime(time.time())
ini_m = str(now_time.tm_mon)
ini_d = str(now_time.tm_mday)
ini_y = str(now_time.tm_year)
#遇到月或日只有一位要補0
if len(ini_m) == 1:
ini_m = "0"+str(ini_m)
if len(ini_d) == 1:
ini_d = "0"+str(ini_d)
get_now_time()
date_picker = TemplateSendMessage(
alt_text='紀錄中...',
template=ButtonsTemplate(
text='西元年/月/日',
title='請選擇日期',
actions=[
DatetimePickerTemplateAction(
label='按我選擇日期',
data='date_postback',
mode='date',#有三種模式可以選
initial=f'{ini_y}-{ini_m}-{ini_d}', #初始化的時間
min='2020-01-01', #時間的上下界
max='2099-12-31'
)
]
)
)
line_bot_api.reply_message(event.reply_token, date_picker)
```
:::info
三種模式
date: 選日期
time: 選時間
datetime: 選日期和時間
:::
之後在Postback函式內要如此取得使用者選擇的日期
```python=
date = event.postback.params['date'] #若是選擇其他mode要跟著更改
print(date)
```
```python
2022-1-1
```
更詳細的語法可以到
https://developers.line.biz/en/reference/messaging-api/ (官方文件)
https://github.com/line/line-bot-sdk-python?fbclid=IwAR1EA6z44YXLTfLXTE0ZaJx0EhQ2izQs1gdYgmrXXc3ko7nkLQpFsauh50g(github範例)
查詢(不過都是英文= =)
### 嵌入googlesheet的範例
### Heroku介紹
Heroku是一個提供免費方案、支援多種程式語言的雲平台,許多人將它用來部署簡單的網頁。
:::success
此處的部署指的是將平常只能在自己電腦上執行的程式碼上傳到遠端的伺服器,由遠端的電腦執行,如此一來可以便能不用開著電腦也能讓做好的機器人持續運作。
:::
由於選擇了Heroku的免費版本,伺服器提供了最多5個網頁的部署;在長時間不運行時,Heroku上的網頁還會進入睡眠狀態以節省資源,呼叫過後需要等待一陣子網頁才會被換醒。
### Line developers/Line bot api
顧名思義 Line developers是Line提供開發者自定義官方機器人的平台,就算是外行人也可以在平台上設定Line機器人的基本功能。不過我們要使用的是其中的webhook功能。
:::success
網頁開發中的"網頁掛鉤"(Webhook)是一種通過自訂回呼(postback)函式來增加或更改網頁表現的方法。
一般情況伺服器與使用者的關係屬於「主從式架構」(Client-server model)。如一般使用Chorme瀏覽器,伺服器等待使用者的請求進行回復。

而若使用webhook,如下圖。

也就是使用函式一來一回的交流來自訂機器人與使用者的互動。
以Webhook架構為基礎的Line bot api是Line官方開發、支援多種程式語言的套件,可以用它內建的函式使機器人能進行更多自動化功能。
:::
以上例子引用自
https://medium.com/@justinlee_78563/line-bot-%E7%B3%BB%E5%88%97%E6%96%87-%E4%BB%80%E9%BA%BC%E6%98%AF-webhook-d0ab0bb192be
### 概念簡介
我們將程式碼上傳至github後,利用按鈕設定好環境,部屬在Heroku伺服器上(Heroku上便有存有line bot環境),並將line後台與heroku連接。
之後我們只要向line機器人傳送訊息,訊息會從使用者傳到line伺服器再傳到heroku,heroku上的程式碼再經由line伺服器回覆訊息給使用者。

### http Errorcode
404 找不到
400 錯誤標籤(拒絕)
200 ok
https://blog.miniasp.com/post/2009/01/16/Web-developer-should-know-about-HTTP-Status-Code
https://ithelp.ithome.com.tw/articles/10250980
### Heroku部署設定檔
將專案部署上Heroku時,我們需要在github資料夾加入一些設定檔,讓Heroku網站能夠判斷各個檔案的功能。
#### README.md
在github專案中,我們通常會用一個.md(markdown)檔案說明專案的用途。
此時,我們將部署專案的按鈕至於此處方便操作。
```mark
[](https://heroku.com/deploy)
```
這個按鈕會自動使用所在位置的儲藏庫進行部署
#### app.py
此py檔就是我們在ngrok上線的主程式
#### Procfile
web: gunicorn app:app
使用gunicron套件打開名稱為app的檔案
#### requirements.txt
這個檔案要放入各種我們要在python檔使用的套件
1-14行是部署linebot的必要套件(大多是網頁框架與連線相關)
如:flask requests lin-bot-api
底下我們另外安裝googlesheet相關的套件
#### app.json
這個檔案要放部署按鈕的設定檔。
```json=
{
"name": "echo bot - LINE Messaging API",
"description": "This is a sample application for the LINE Messaging API",
"keywords": ["chatbot", "line", "python"],
"env": {
"CHANNEL_ACCESS_TOKEN": {
"description": "LINE bot Channel access token",
"required": true
},
"CHANNEL_SECRET": {
"description": "LINE bot Channel secret",
"required": true
}
}
}
```
會對應到部署畫面的樣式

此檔案中比較重要的是env這個鍵。
範例中提供了兩個環境變數的輸入。
#### googlesheet金鑰名稱.json
若要讀寫自己的google sheet,還要上傳之前取得的金鑰到資料夾內。
#### @ (decorator)
function decorator是一個python特別的語法,用來將函式們進行封裝、加工並保持語法的簡潔與美觀。它常用來對不同函式套用同一組功能(如下的範例),在使用模組時也常常見到。
```python
def print_function(func):
def wrapper():
print(func)
func()
return wrapper #作為裝飾器的函式必須回傳一個函式,程式會在呼叫原函式時呼叫它。
@print_function #裝飾器的添加
def Hello(): #原函式
print("Hello World!")
@print_function
def Python():
print("Python is useful!")
Hello()
Python()
```
```text=
<function Hello at 0x00000121BDD92E18>
Hello World!
<function Python at 0x00000121BDD92A60>
Python is useful!
```
:::info
在呼叫Hello()時,print_function()這個函式奪走了Hello()的主控權,將Hello()函式做為print_function()的參數(設為func)。
print_function()中,定義了wrapper(),wrapper()先印出了Hello(),然後才呼叫Hello()本身。
當然decorator對Python()這個函式也是同理
:::