---
title: 1207 對話機器人(4/5)
tags: GCP, 筆記
---
https://annieshing.pixnet.net/blog/post/460198511
AI:高運算力 資料
私有機房
共享機房 ->雲服務 驗證可行
colab
環境快速重來過 :套件可追朔
要用就瞬間開起來 不要用就瞬間關掉 資源不浪費
GCP 3個月300美金
AI跟雲服務有很大的相關性
雲原生
雲服務體驗流程
---
## gcp的三大核心服務:
### 資料儲存:cloud storage 無限資料 (儲存)
### 資料運算:compute engine (操作)
### 權限應用: IAM (權限)
---
## 實作:

### 1 .搜尋GCP

### 2. 頁面右上方點選『控制台』後會進入到下列畫面⬇︎

### ->按下紅框區域,創建新的專案

### 3. 專案取名,記得是無機構->建立

### 若要更換語言:右上方『偏好設定』裡設定成英文
### 建立完成後,點選上方如圖->點選剛創好的專案

### 點選左欄位的->Storage->Browser

### 創建Bucket才能裝所需服務

### 命名Bucket->continue

### 決定資料儲存地區:通常選Region->asia-east-taiwan存台灣比較便宜->continue

### 越常存取選上方 不常存取選下方 這邊先選Standard

### 資料權限:有兩種 精細跟統一 這邊示範統一:權限統一收管

### 加密->create

### 成功創建Bucket->上傳檔案 upload files

### 操作GCP可用內部的編輯器 跟外部程式來操作 這邊示範內建的:按右上方開啟 cloud shell

### 開啟後按下open editor 之後往上拉

### 建立一個py檔 用此來操作查看gcp內的檔案


### 使用程式碼查看list objects
輸入以下程式碼->可從下方網站找到程式碼->更改bucket name(專案名稱)
#### 1. 進入網站 選python 複製下方程式碼至cloud shell
* [Listing objects | Cloud Storage | Google Cloud](https://cloud.google.com/storage/docs/listing-objects#code-samples)
#### 2. 如圖示

#### 3. 複製到cloud shell 縮排 更改bucket name(跟專案名稱)
In GCP 內的範例程式碼
```
# 我要用python 去瀏覽桶子內有什麼檔案
# python cloud storage list all object in bucket
from google.cloud import storage
"""Lists all the blobs in the bucket."""
bucket_name = "自己的bucket名稱id:ai-cxcxc-demo"
storage_client = storage.Client()
# Note: Client.list_blobs requires at least package version 1.17.0.
blobs = storage_client.list_blobs(bucket_name)
for blob in blobs:
print(blob.name)
```

### 開啟terminal


### 輸入下方指令:把shell加進服務內(名稱是專案名的ID)

p.s可以查看ID 名字小寫 不重複

### 授權

### 程式碼在專案中才可以執行(有黃色專案名稱表示有加進去
->這邊是示範專案內操作使用

### IAM權限使用:
#### 選擇IAM->Service Accounts->+create service account


### 創service account

### 給出有cloud storage的管理之權
> 權限管理可以有很細部的討論,同學在專題後可以再花時間研究
> 這邊先示範選cloud storage->storage admin
### p.s storage admin是最高權限 通常不會給到這麼大 這邊是示範


### 未來其他使用者要操作時 可加入 課堂先不示範

### 有了service accunt還要有key才能調度資料:新增key
#### 點剛剛創的名稱

### 點選create new key

### 按create

### key會存成一個json檔->改名成service_account(之後好操作)


### 使用外部程式操作:colab
要給json檔 +輸入》程式碼 才能從外部操作GCP
程式碼可參考共筆:* [004-2020-1207-第四天 - Google 文件](https://docs.google.com/document/d/1F7hN_VAtn-VzZeO5l1wyhhMX6ebvimyFAXVwBYcRoTw/edit)找到第二段有加上from_service_account_json('service_account.json')這段⬇︎
```
# 我要用python 去瀏覽桶子內有什麼檔案
# python cloud storage list all object in bucket
from google.cloud import storage
"""Lists all the blobs in the bucket."""
bucket_name = "ai-cxcxc-demo"
storage_client = storage.Client.from_service_account_json('service_account.json')
# Note: Client.list_blobs requires at least package version 1.17.0.
blobs = storage_client.list_blobs(bucket_name)
for blob in blobs:
print(blob.name)
---
```
### colab 執行:查看cloud storage內的檔案
1. 程式碼
2. 上傳service_account.json至colab
3. 執行後可以查看cloud storage內的檔案
換句話說 在外部也可使用程式的操作來使用GCP

------

已媒人為例,跟遠端服務溝通,都有隔壁大媽(客戶端)來溝通
要跟外部做互動 找程式碼先找客戶端
## 017 檔案操作示範
## 跟cloud storage作互動
* 先建個客戶端
* 用客戶端去:
上傳物件
瀏覽物件
下載物件
刪除物件
https://github.com/BingHongLi/ai-chatbot-tutorial/blob/main/017_%E8%8D%8A%E7%84%A1%E5%91%BD%E7%9A%84%E5%B7%A6%E6%89%8B%E5%8A%8D.ipynb
## 1. 建立客戶端 (必須要上傳service_account.json檔)
```
# step1 建立客戶端
# gcp cloud storage client python example
# 017就有了啦(以下複製017)
from google.cloud import storage
# 創建客戶端,由於在專案外,必須給一個service_account.json檔
storage_client = storage.Client.from_service_account_json('service_account.json')
```

## 2.用客戶端去上傳物件:圖中圈出來的要更改
```
# 用cloud storage的客戶端上傳物件
# python cloud storage upload object example
# 017就有了啦
# 上傳到指定的桶子內
# https://cloud.google.com/storage/docs/uploading-objects#storage-upload-object-code-sample
bucket_name='要改成自己的 如:ai-cxcxc-demo'
source_file_name= '要上傳檔案的名字13080001127357.jpg'
# object的名子
destination_blob_name= '上傳雲端要叫檔案名為:123/456.jpg'
# 指定桶子
bucket = storage_client.bucket(bucket_name)
# 指定本地檔案
blob = bucket.blob(destination_blob_name)
# 指定遠端位置
blob.upload_from_filename(source_file_name)
print(
"File {} uploaded to {}.".format(
source_file_name, destination_blob_name
)
)
```

## 3 瀏覽物件: 執行完成後,至GCP確認檔案是否有成功上傳
### Storage->Browser

### 點選123資料夾

### 資料有成功上傳

## 4. 下載物件:先設定權限公開 才可以下載
```
精細:每個檔案有一個權限
統一:檔案都統一權限
雲端資料預設都是不公開
```
### 選擇premission來調整權限

### 下方黃色區域表示在專案中擁有權限的人->按下ADD

### 讓所有人都可以看到物件:選擇AllUsers->選擇Cloud Storage->Storage object viewer

AllUsers 只能看 不能設定有操作權限

### 確認:此動作後資料會公開

### 完成後,會顯示資料是公開狀態(因前面是設定統一,所以設定後所有檔案都會是公開)


### 這邊選擇任一檔案進入後,就能獲得public url,進入頁面後就可以下載資料了

```
物件的權限調整
調整公開之後,物件都會有一個https連結
https://storage.googleapis.com/ai-cxcxc-demo/123/456.jpg
得到的url規律:
https://storage.googleapis.com/ + bucket_name + object_name
```
## 5 刪除物件:
新增程式碼:更改bucket_name和要刪除的檔案名稱

```
# 刪除桶子內的物件
# https://cloud.google.com/storage/docs/deleting-objects
bucket_name = "這邊要改your-bucket-name"
blob_name = "這邊要改your-object-name"
bucket = storage_client.bucket(bucket_name)
blob = bucket.blob(blob_name)
blob.delete()
print("Blob {} deleted.".format(blob_name))
```
----
## 示範LineChatBot 與GCP結合
<!-- google 估得好 工程師當到老 -->
:::info
#### 用戶上傳照片到我方的機器人裡面,機器人將照片傳入cloud storge 內,並依照用戶id做資料夾
程式碼pattern:
* 安裝套件
* 引用套件
* 應用伺服器準備
* 消息素材準備
* 應用方法設計
* 伺服器起啟動
:::
### 1.安裝套件
```
!pip install line-bot-sdk flask flask-ngrok
```

---
### 2.引用套件
```
'''
引用套件
'''
# 引用Web Server套件
from flask import Flask, request, abort, jsonify
# 載入json處理套件
import json
# 外部連結自動生成套件
from flask_ngrok import run_with_ngrok
# 從linebot 套件包裡引用 LineBotApi 與 WebhookHandler 類別
from linebot import (
LineBotApi, WebhookHandler
)
# 引用無效簽章錯誤
from linebot.exceptions import (
InvalidSignatureError
)
```
----

----
### 3.應用伺服器準備:app, handler,line-bot-api,storage-client
登入line api 更改CHANNEL_SECRET/CHANNEL_ACCESS_TOKEN
* [Messaging API 介紹 | LINE Developers](https://developers.line.biz/zh-hant/docs/messaging-api/overview/)
```
'''
建置主程序
建置handler與 line_bot_api
'''
# 設定Server啟用細節
app = Flask(__name__,static_url_path = "/material" , static_folder = "./material/")
run_with_ngrok(app)
# 生成實體物件
line_bot_api = LineBotApi("要改CHANNEL_ACCESS_TOKEN“)
handler = WebhookHandler("要改CHANNEL_SECRET")
from google.cloud import storage
# 本地端才如此作為,最好改用環境變數
storage_client = storage.Client.from_service_account_json('service_account.json')
```

---
### 4.主程序的入口:讓line把消息傳過來,並依照我們設定處理
```
'''
建置主程序的API入口
接受Line傳過來的消息
並取出消息內容
將消息內容存在google drive的檔案內
並請handler 進行消息驗證與轉發
'''
# 啟動server對外接口,使Line能丟消息進來
@app.route("/", methods=['POST'])
def callback():
# get X-Line-Signature header value
signature = request.headers['X-Line-Signature']
# get request body as text
body = request.get_data(as_text=True)
print(body)
# 記錄用戶log,請去查更正確的logging作法
f = open("./ai-event.log", "a")
f.write(body)
f.close()
# handle webhook body
try:
handler.handle(body, signature)
except InvalidSignatureError:
abort(400)
return 'OK'
```

----
### 5.業務方法處理:收到圖片消息的時候,把圖片抓回來,再上傳到cloud storage內(bucket_name要改GCP要存的專案id名稱)
```
'''
若收到圖片消息時,
先回覆用戶文字消息,並從Line上將照片拿回。
'''
from linebot.models import(
MessageEvent,ImageMessage, TextSendMessage
)
# 吿知handler 收到消息事件時 且為圖片消息 做下面的方法
@handler.add(MessageEvent, message=ImageMessage)
def handle_message(event):
#回覆用戶
line_bot_api.reply_message(
event.reply_token,
TextSendMessage(text='Image has Upload'+ ' ' + event.message.id))
#請line_bot-api把圖片抓回來
message_content = line_bot_api.get_message_content(event.message.id)
with open(event.message.id+'.jpg', 'wb') as fd:
for chunk in message_content.iter_content():
fd.write(chunk)
#指定上傳位置
bucket_name='your_gcp_bucket_name'
source_file_name= event.message.id+'.jpg'
destination_blob_name= event.source.user_id +'/'+ event.message.id+'.jpg'
bucket = storage_client.bucket(bucket_name)
blob = bucket.blob(destination_blob_name)
blob.upload_from_filename(source_file_name)
```

### 6.伺服器啟動(將更新網址回傳到Line Webhook URL使用)

### 7.執行後,用戶從line機器人上傳照片會出現在左邊以及在Cloud Storage內會自動依照用戶id存成資料夾。

-----
## 解析google Text-to-Speech使用,若要直接應用在Line Chatbot可看老師019程式碼
### 1.先到GCP搜尋->按下啟用


### 2. 調度ai的範例 (參考老師019範例或共筆4內程式碼)
將下方程式碼複製至colab
```
# 安裝之後,點擊重新執行階段
!pip install google-cloud-texttospeech
from google.cloud import texttospeech
# Instantiates a client
client = texttospeech.TextToSpeechClient.from_service_account_json('service_account.json')
# Set the text input to be synthesized
synthesis_input = texttospeech.SynthesisInput(text="班代,每次都拍你,你是不是很開心?")
# Build the voice request, select the language code ("en-US") and the ssml
# voice gender ("neutral")
voice = texttospeech.VoiceSelectionParams(
language_code='zh-TW',
ssml_gender=texttospeech.SsmlVoiceGender.NEUTRAL)
# Select the type of audio file you want returned
audio_config = texttospeech.AudioConfig(
audio_encoding=texttospeech.AudioEncoding.LINEAR16)
# Perform the text-to-speech request on the text input with the selected
# voice parameters and audio file type
response = client.synthesize_speech(input=synthesis_input, voice=voice, audio_config=audio_config)
# The response's audio_content is binary.
with open('output.wav', 'wb') as out:
# Write the response to the output file.
out.write(response.audio_content)
print('Audio content written to file "output.wav"')
```
### 重新啟動並執行


### 按下圖中按鈕

## 執行成功後 檔案內會出現剛剛文字輸入的音檔

-----
## 更換聲音:從SsmlVoiceGender查看原始碼

若要女聲,將程式碼從
```
ssml_gender=texttospeech.SsmlVoiceGender.NEUTRAL) 更改成
```
```
ssml_gender=texttospeech.SsmlVoiceGender.FEMALE)
```

-----
## 將AI導入Line Chatbot:google Text-to-Speech:使用019實際操作,將需要改的名稱填上,就能使用了!
* [ai-chatbot-tutorial/019_GCP與Chatbot_2.ipynb at main · BingHongLi/ai-chatbot-tutorial](https://github.com/BingHongLi/ai-chatbot-tutorial/blob/main/019_GCP%E8%88%87Chatbot_2.ipynb)
```
!pip install line-bot-sdk flask flask-ngrok
# 安裝後必須點擊Runtime -> Restart Runtime
!pip install google-cloud-texttospeech
```
```
'''
引用套件
'''
# 引用Web Server套件
from flask import Flask, request, abort, jsonify
# 載入json處理套件
import json
# 外部連結自動生成套件
from flask_ngrok import run_with_ngrok
# 從linebot 套件包裡引用 LineBotApi 與 WebhookHandler 類別
from linebot import (
LineBotApi, WebhookHandler
)
# 引用無效簽章錯誤
from linebot.exceptions import (
InvalidSignatureError
)
```
```
'''
建置主程序
建置handler與 line_bot_api
'''
# 設定Server啟用細節
app = Flask(__name__,static_url_path = "/material" , static_folder = "./material/")
run_with_ngrok(app)
# 生成實體物件
line_bot_api = LineBotApi("CHANNEL_ACCESS_TOKEN")
handler = WebhookHandler("CHANNEL_SECRET")
from google.cloud import storage
from google.cloud import texttospeech
# 本地端才如此作為,最好改用環境變數
storage_client = storage.Client.from_service_account_json('service_account.json')
bucket_name='GCP Cloud Storage Bucket name'
# Instantiates a client
speech_client = texttospeech.TextToSpeechClient.from_service_account_json('service_account.json')
```
```
'''
建置主程序的API入口
接受Line傳過來的消息
並取出消息內容
將消息內容存在google drive的檔案內
並請handler 進行消息驗證與轉發
'''
# 啟動server對外接口,使Line能丟消息進來
@app.route("/", methods=['POST'])
def callback():
# get X-Line-Signature header value
signature = request.headers['X-Line-Signature']
# get request body as text
body = request.get_data(as_text=True)
print(body)
# 記錄用戶log,請去查更正確的logging作法
f = open("./ai-event.log", "a")
f.write(body)
f.close()
# handle webhook body
try:
handler.handle(body, signature)
except InvalidSignatureError:
abort(400)
return 'OK'
```
```
'''
若收到圖片消息時,
先回覆用戶文字消息,並從Line上將照片拿回。
'''
from linebot.models import(
MessageEvent,ImageMessage, TextSendMessage
)
@handler.add(MessageEvent, message=ImageMessage)
def handle_message(event):
line_bot_api.reply_message(
event.reply_token,
TextSendMessage(text='Image has Upload'+ ' ' + event.message.id))
message_content = line_bot_api.get_message_content(event.message.id)
with open(event.message.id+'.jpg', 'wb') as fd:
for chunk in message_content.iter_content():
fd.write(chunk)
source_file_name= event.message.id+'.jpg'
destination_blob_name= event.source.user_id +'/'+ event.message.id+'.jpg'
bucket = storage_client.bucket(bucket_name)
blob = bucket.blob(destination_blob_name)
blob.upload_from_filename(source_file_name)
```
```
'''
若收到文字消息時
將文字傳給GCP,轉譯成音訊後
放到cloud storage,並組合成AudioSendMessage,傳回給用戶
Podcast
'''
from linebot.models import(
MessageEvent,TextMessage, AudioSendMessage
)
@handler.add(MessageEvent, message=TextMessage)
def handle_text_message(event):
# Set the text input to be synthesized
synthesis_input = texttospeech.SynthesisInput(text=event.message.text)
# Build the voice request, select the language code ("en-US") and the ssml
# voice gender ("neutral")
voice = texttospeech.VoiceSelectionParams(
language_code="zh-TW", ssml_gender=texttospeech.SsmlVoiceGender.NEUTRAL
)
# Select the type of audio file you want returned
audio_config = texttospeech.AudioConfig(
audio_encoding=texttospeech.AudioEncoding.MP3
)
# Perform the text-to-speech request on the text input with the selected
# voice parameters and audio file type
response = speech_client.synthesize_speech(
input=synthesis_input, voice=voice, audio_config=audio_config
)
# The response's audio_content is binary.
voice_mp3_file_path=event.message.id+".mp3"
with open(voice_mp3_file_path, "wb") as out:
# Write the response to the output file.
out.write(response.audio_content)
print('Audio content written to file '+voice_mp3_file_path)
# 上傳檔案
destination_blob_name= event.source.user_id +'/'+ event.message.id+'.mp3'
bucket = storage_client.bucket(bucket_name)
blob = bucket.blob(destination_blob_name)
blob.upload_from_filename(voice_mp3_file_path)
# 回話
line_bot_api.reply_message(
event.reply_token,
AudioSendMessage(
original_content_url ="https://storage.googleapis.com/"+bucket_name+'/'+destination_blob_name,
duration=60000
)
)
```
```
# 主程序運行
app.run()
```
Google ML 參考
https://colab.research.google.com/github/robeartoe/APIWorkshop/blob/master/MLWorkshop.ipynb#scrollTo=Snq8ehAu7JkD
-----
## 用Json檔轉成消息給用戶
```
!pip install line-bot-sdk flask flask-ngrok
```
```
'''
引用套件
'''
# 引用Web Server套件
from flask import Flask, request, abort, jsonify
# 載入json處理套件
import json
# 外部連結自動生成套件
from flask_ngrok import run_with_ngrok
# 從linebot 套件包裡引用 LineBotApi 與 WebhookHandler 類別
from linebot import (
LineBotApi, WebhookHandler
)
# 引用無效簽章錯誤
from linebot.exceptions import (
InvalidSignatureError
)
```
```
''
建置主程序
建置handler與 line_bot_api
'''
# 設定Server啟用細節
app = Flask(__name__,static_url_path = "/material" , static_folder = "./material/")
run_with_ngrok(app)
# 生成實體物件
line_bot_api = LineBotApi("CHANNEL_ACCESS_TOKEN")
handler = WebhookHandler("CHANNEL_SECRET")
```
```
'''
建置主程序的API入口
接受Line傳過來的消息
並取出消息內容
將消息內容存在google drive的檔案內
並請handler 進行消息驗證與轉發
'''
# 啟動server對外接口,使Line能丟消息進來
@app.route("/", methods=['POST'])
def callback():
# get X-Line-Signature header value
signature = request.headers['X-Line-Signature']
# get request body as text
body = request.get_data(as_text=True)
print(body)
# 記錄用戶log,請去查更正確的logging作法
f = open("./ai-event.log", "a")
f.write(body)
f.close()
# handle webhook body
try:
handler.handle(body, signature)
except InvalidSignatureError:
abort(400)
return 'OK'
```
```
'''
line bot designer 的json字串檔
我們能不能判斷 json的type欄位
'''
demo_message_json_string='''
{
"type":"location",
"title":"test",
"address":"test",
"latitude":35.65910807942215,
"longitude":139.70372892916203
}
'''
```
```
import json
demo_message_json=json.loads(demo_message_json_string)
```
```
'''
判斷Json檔的type格式 如果為text 那就用TextSendMessage 回應
如果為location 那就用LocationSendMessage回應
'''
from linebot. models import (ImageSendMessage, TextSendMessage, LocationSendMessage)
from linebot.models import (TextSendMessage,LocationSendMessage)
if demo_message_json.get('type')=='text':
reply_text_send_message=TextSendMessage.new_from_json_dict(demon_message_json)
elif demo_message_json.get('type')=='location':
reply_send_message=LocationSendMessage.new_from_json_dict(demon_message_json)
elif demo_message_json.get('type')=='image':
reply_send_message= ImageSendMessage.new_from_json_dict(demon_message_json)
```
```
from linebot.models import (MessageEvent, TextMessage)
@handler.add(MessageEvent, message=TextMessage)
def handle_text_message(event):
line_bot_api.reply_message(
event.reply_token,
text_send_message
)
```
```
app.run()
```
line bot designer 的json字串檔
上方程式碼執行後,當用戶端傳訊息給機器人後,下方會出現json檔
把這些程式碼丟到Json Formatter可以方便看出格式
* [JSON Formatter & Validator](https://jsonformatter.curiousconcept.com/#)
*


------
### 小補充
* 創立專案:
名稱 跟ID通常一樣 除非有特別改
project 任何服務都不能執行!(空盤子)!換成無機構 不要用學校的
* 公司一個項目 建立一個GCP專案
* 設立預算 (以防被盜後有些保障)
* 預算與快訊-> 設定預算
* service account 需要key 才能操作
----
### cloud storage
應用場景
巨量資料
空間無限的儲存服務
設計思路 核心名詞
資源object 裝載容器 bucket
水 水杯
錢 錢包
鉛筆 鉛筆盒
創桶子 資料所在地 storage-level 1
取用權限
加密
---

# END