├── 001_http-server介紹.ipynb ├── 002_自主練習flask.ipynb ├── 003_LineChatbot基本組成_關注事件.ipynb ├── 004_自主練習.ipynb ├── 005_LineChatbot基本組成_文字消息的回覆.ipynb ├── 006_自主練習.ipynb ├── 007_LineChatbot基本組成_範本訊息.ipynb ├── 008_自主練習.ipynb ├── 009_LineChatbot基本組成_範本訊息的缺點.ipynb ├── 010_自主練習.ipynb ├── 011_LineChatbot基本組成_下載多媒體訊息.ipynb ├── 011_自主練習.ipynb ├── 012_LineChatbot綜合.ipynb ├── 012_自主練習.ipynb ├── 013_把line_bot_designer的消息變成json.ipynb ├── 013_自主練習.ipynb ├── 014_自主練習.ipynb ├── 014_訊息推播.ipynb ├── 015_圖文選單建置.ipynb ├── 016_teachable-machine_chatbot.ipynb ├── 017_荊無命的左手劍.ipynb ├── 018_GCP與Chatbot.ipynb ├── 019_GCP與Chatbot_2.ipynb ├── 020_Chatbot資料清洗.ipynb ├── README.md └── blockchain_demo.jpg /001_http-server介紹.ipynb: -------------------------------------------------------------------------------- 1 | {"cells":[{"attachments":{},"cell_type":"markdown","metadata":{"id":"UMSn0hQEqmMW"},"source":["人類世界是一個龐大的訊息系統,每個人都是訊息的傳入與傳出點\n","\n","有些人眉目傳情\n","\n","有些人悄然攜手\n","\n","總而言之,有很多傳遞方式"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"JSnLGIcEqZ04"},"outputs":[],"source":[]},{"attachments":{},"cell_type":"markdown","metadata":{"id":"FSsnOYzxrQAG"},"source":["資訊世界也差不多,每個電腦都可以是訊息的傳入與傳出點\n","\n","有的時候,我們想收發email,會用smtp\n","\n","有的時候,想跟別人共享檔案,會用ftp\n","\n","而最常見的一種方式,是http,常見的網站協定"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"xuB6ROBQq5Rr"},"outputs":[],"source":[]},{"attachments":{},"cell_type":"markdown","metadata":{"id":"GpnIVLxMrvOl"},"source":["每一種程式語言都能夠用套件,去表達http協定\n","\n","python語言最常見的一種套件,叫做flask\n","\n","現在我們就來體驗一下吧"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"QyiX25M9r96i"},"outputs":[],"source":[]},{"attachments":{},"cell_type":"markdown","metadata":{"id":"9j3rjoLAr-CH"},"source":["體驗流程如下,但凡寫程式的流程似乎就這樣了\n","\n","安裝套件\n","\n","引用套件\n","\n","建置主程序\n","\n","為主程序製作交互API接口\n","\n","啟動主程序"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"Zlxxf_XHsrQW"},"outputs":[],"source":["'''\n","安裝套件\n","'''\n","!pip install line-bot-sdk flask \n","\n","!pip install pyngrok\n","\n","!ngrok authtoken 你的ngrok authtoken!!!!"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"QpiUk0oqs2xA"},"outputs":[],"source":["'''\n","引用套件\n","'''\n","\n","# 引用flask Web Server套件\n","from flask import Flask, request, abort, jsonify\n","\n","# 載入json處理套件\n","import json\n","\n","# 外部連結自動生成套件\n","# from flask_ngrok import run_with_ngrok\n","from pyngrok import ngrok\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"qIz_GY8JtGtR"},"outputs":[],"source":["'''\n","建置主程序\n","'''\n","\n","# 設定Server啟用細節\n","app = Flask(__name__,static_url_path = \"/material\" , static_folder = \"./material/\")\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"PyxwcE731IRD"},"outputs":[],"source":["'''\n","\n","為主程序創造多個API接口\n","\n","想像思路\n","當外面的封包進來的時候,轉流到這個方法\n","\n","'''\n","# 啟動主程序的API接口,最簡單版本\n","@app.route(\"/\", methods=['GET'])\n","def callback():\n"," \n"," return 'Hello World'"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"UlZwRkCS1pmY"},"outputs":[],"source":["# 第二隻接口,\n","\n","# 啟動主程序的API接口,名為calculate,計算機功能,剖析v1參數與v2參數\n","@app.route(\"/calculate\", methods=['GET'])\n","def calculate():\n"," first_parameter = int(request.args.get('v1'))\n"," second_parameter = int(request.args.get('v2'))\n"," return str(first_parameter+second_parameter)"]},{"cell_type":"code","execution_count":7,"metadata":{"executionInfo":{"elapsed":4,"status":"ok","timestamp":1606031164870,"user":{"displayName":"李秉鴻","photoUrl":"","userId":"05108415858439526086"},"user_tz":-480},"id":"htiCzxi82fN3"},"outputs":[],"source":["# 主程序運行\n","port = 5000\n","# Open a ngrok tunnel to the HTTP server\n","public_url = ngrok.connect(port).public_url\n","print(\" * ngrok tunnel \\\"{}\\\" -> \\\"http://127.0.0.1:{}\\\"\".format(public_url, port))\n","app.run()"]},{"cell_type":"code","execution_count":null,"metadata":{"executionInfo":{"elapsed":2,"status":"ok","timestamp":1606031164873,"user":{"displayName":"李秉鴻","photoUrl":"","userId":"05108415858439526086"},"user_tz":-480},"id":"A2icnYNS6Oqb"},"outputs":[],"source":[]}],"metadata":{"colab":{"authorship_tag":"ABX9TyMX4Iwtn21MjCEuKnrReHJf","name":"001_http-server介紹.ipynb","provenance":[]},"kernelspec":{"display_name":"Python 3","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.10.4"}},"nbformat":4,"nbformat_minor":0} 2 | -------------------------------------------------------------------------------- /002_自主練習flask.ipynb: -------------------------------------------------------------------------------- 1 | {"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"002_自主練習flask.ipynb","provenance":[],"authorship_tag":"ABX9TyOVGMIbCNjnT+6qm9HSocXE"},"kernelspec":{"name":"python3","display_name":"Python 3"}},"cells":[{"cell_type":"markdown","metadata":{"id":"eIWC3DaS671N"},"source":["當初練習程式的時候,真的是手把手把好幾本書的範例打完\n","\n","雖然現在都忘光了,但是在打的過程中,那些有形的,都給熬成了無形的,融入了血肉\n","\n","感覺就自然來了,請把剛剛的範例打一遍"]},{"cell_type":"code","metadata":{"id":"l_YVWPkA7fTN"},"source":[""],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"l4k9E9Dv7ct0"},"source":["\n","安裝套件\n","\n","引用套件\n","\n","建置主程序\n","\n","為主程序製作交互API接口\n","\n","啟動主程序\n"]},{"cell_type":"code","metadata":{"id":"DEhgeZyL7ac2"},"source":["'''\n","安裝套件\n","\n","line-bot-sdk, flask, flask-ngrok 是啥,自己猜啊,自己google啊,記得養成這股自己找的味,外面的程式碼就會了\n","\n","'''\n","!pip install line-bot-sdk flask flask-ngrok"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"MWSi9FGB7iZK"},"source":["'''\n","引用套件\n","\n","將要用的套件引入\n","\n","萬一不知道自己要引入什麼套件,也沒關係,邊開發邊引入,那就好了\n","\n","在未來的某一天,再把它重組,放在一起\n","\n","'''"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"KfiM2vdx8A9h"},"source":["'''\n","\n","建置主程序\n","\n","'''"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"DwclGqfC8DoP"},"source":["'''\n","\n","為主程序創造兩個API接口\n","\n","想像思路\n","當外面的封包進來的時候,轉流到這個方法\n","\n","'''"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"1GWoOO1b8HC3"},"source":["'''\n","\n","第二隻接口,\n","\n","啟動主程序的API接口,名為calculate,計算機功能,剖析v1參數與v2參數\n","\n","'''"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"y56_y9xw8TIH"},"source":["# 啟動主程序\n"],"execution_count":null,"outputs":[]}]} -------------------------------------------------------------------------------- /003_LineChatbot基本組成_關注事件.ipynb: -------------------------------------------------------------------------------- 1 | {"cells":[{"cell_type":"code","execution_count":null,"metadata":{"id":"ylkDvkEfEZIw"},"outputs":[],"source":["'''\n","\n","掛載Google硬碟\n","安裝套件\n","引用套件\n","APP應用準備\n","消息準備\n","handler執行方法設計\n","啟動應用\n","\n","'''"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"2mDqhB78Fxp6"},"outputs":[],"source":["'''\n","\n","資料 mapping 至google drive\n","\n","把資料寫在/content/drive\n","\n","即可保存在 google drive內\n","\n","'''\n","from google.colab import drive\n","drive.mount('/content/drive')"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"4iGRs1oYD6Ca"},"outputs":[],"source":["'''\n","\n","流程解析\n","\n","'''\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"JyhJqq8KImyR"},"outputs":[],"source":["# 安裝套件\n","!pip install line-bot-sdk flask \n","!pip install pyngrok"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"rJDNE2TYEaYA"},"outputs":[],"source":["'''\n","引用套件\n","'''\n","\n","# 引用Web Server套件\n","from flask import Flask, request, abort, jsonify\n","\n","# 載入json處理套件\n","import json\n","\n","# 外部連結自動生成套件\n","# from flask_ngrok import run_with_ngrok\n","from pyngrok import ngrok\n","\n","# 從linebot 套件包裡引用 LineBotApi 與 WebhookHandler 類別\n","from linebot import (\n"," LineBotApi, WebhookHandler\n",")\n","\n","# 引用無效簽章錯誤\n","from linebot.exceptions import (\n"," InvalidSignatureError\n",")\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"XssY97Y9Eahe"},"outputs":[],"source":["'''\n","建置主程序app\n","\n","建置handler與 line_bot_api\n","'''\n","\n","# 設定Server啟用細節\n","app = Flask(__name__,static_url_path = \"/material\" , static_folder = \"./material/\")\n","\n","\n","# 生成實體物件\n","line_bot_api = LineBotApi(\"Channel_Access_token\")\n","handler = WebhookHandler(\"Channel_Secret\")"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"jyIFMBi4EvTl"},"outputs":[],"source":["'''\n","建置主程序的API入口\n"," 接受Line傳過來的消息\n"," 並取出消息內容\n"," 將消息內容存在google drive的檔案內\n"," 並請handler 進行消息驗證與轉發\n","'''\n","\n","# 啟動server對外接口,使Line能丟消息進來\n","@app.route(\"/\", methods=['POST'])\n","def callback():\n"," # get X-Line-Signature header value\n"," signature = request.headers['X-Line-Signature']\n","\n"," # get request body as text\n"," body = request.get_data(as_text=True)\n"," print(body)\n","\n"," # 記錄用戶log\n"," f = open(\"/content/drive/MyDrive/ai-event.log\", \"a\")\n"," f.write(body)\n"," f.close()\n","\n"," # handle webhook body\n"," try:\n"," handler.handle(body, signature)\n"," except InvalidSignatureError:\n"," abort(400)\n","\n"," return 'OK'"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"YhyKB6njFHJC"},"outputs":[],"source":["\n","'''\n","\n","撰寫用戶關注時,我們要處理的商業邏輯\n","\n","1. 取得用戶個資,並存回伺服器\n","2. 回應用戶,歡迎用的文字消息\n","\n","'''\n","\n","\n","# 載入Follow事件\n","from linebot.models.events import (\n"," FollowEvent\n",")\n","\n","from linebot.models import(\n"," TextSendMessage, ImageSendMessage\n",")\n","\n","# 告知handler,如果收到FollowEvent,則做下面的方法處理\n","@handler.add(FollowEvent)\n","def reply_text_and_get_user_profile(event):\n"," \n"," # 取出消息內User的資料\n"," user_profile = line_bot_api.get_profile(event.source.user_id)\n"," \n"," # 將用戶資訊存在檔案內\n"," with open(\"/content/drive/MyDrive/users.txt\", \"a\") as myfile:\n"," myfile.write(json.dumps(vars(user_profile),sort_keys=True))\n"," myfile.write('\\n')\n"," \n"," # 回覆文字消息與圖片消息\n"," line_bot_api.reply_message(\n"," event.reply_token,\n"," [TextSendMessage('安安,你的個資已被我紀錄了')]\n"," )"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"background_save":true},"id":"NVkYb65fHY9W"},"outputs":[],"source":["# 主程序運行\n","port = 5000\n","# Open a ngrok tunnel to the HTTP server\n","public_url = ngrok.connect(port).public_url\n","print(\" * ngrok tunnel \\\"{}\\\" -> \\\"http://127.0.0.1:{}\\\"\".format(public_url, port))\n","app.run()"]}],"metadata":{"colab":{"authorship_tag":"ABX9TyPyJ8OBDQaQj1RLLnlnFoo5","collapsed_sections":[],"mount_file_id":"1_Qz4yVKsgaS6lF2vvbz1_nIzNdg4zA4v","name":"003_LineChatbot基本組成_關注事件.ipynb","provenance":[]},"kernelspec":{"display_name":"Python 3","name":"python3"}},"nbformat":4,"nbformat_minor":0} 2 | -------------------------------------------------------------------------------- /004_自主練習.ipynb: -------------------------------------------------------------------------------- 1 | {"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"004_自主練習.ipynb","provenance":[],"authorship_tag":"ABX9TyMCo8TgVDXQP/rg9vvjTEj2"},"kernelspec":{"name":"python3","display_name":"Python 3"}},"cells":[{"cell_type":"code","metadata":{"id":"9JhCUdg9jsDj"},"source":[""],"execution_count":null,"outputs":[]}]} -------------------------------------------------------------------------------- /005_LineChatbot基本組成_文字消息的回覆.ipynb: -------------------------------------------------------------------------------- 1 | {"cells":[{"cell_type":"code","execution_count":null,"metadata":{"id":"ylkDvkEfEZIw"},"outputs":[],"source":["'''\n","掛載Google硬碟\n","安裝套件\n","引用套件\n","APP應用準備\n","消息素材準備\n","handler執行方法設計\n","啟動應用\n","\n","\n","'''"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"2mDqhB78Fxp6"},"outputs":[],"source":["'''\n","\n","資料 mapping 至google drive\n","\n","把資料寫在/content/drive\n","\n","即可保存在 google drive內\n","\n","'''\n","from google.colab import drive\n","drive.mount('/content/drive')"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"4iGRs1oYD6Ca"},"outputs":[],"source":["'''\n","\n","流程解析\n","\n","'''\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"JyhJqq8KImyR"},"outputs":[],"source":["!pip install line-bot-sdk flask\n","!pip install pyngrok\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"rJDNE2TYEaYA"},"outputs":[],"source":["'''\n","引用套件\n","'''\n","\n","\n","# 引用Web Server套件\n","from flask import Flask, request, abort, jsonify\n","\n","# 載入json處理套件\n","import json\n","\n","# 外部連結自動生成套件\n","# from flask_ngrok import run_with_ngrok\n","from pyngrok import ngrok\n","\n","# 從linebot 套件包裡引用 LineBotApi 與 WebhookHandler 類別\n","from linebot import (\n"," LineBotApi, WebhookHandler\n",")\n","\n","# 引用無效簽章錯誤\n","from linebot.exceptions import (\n"," InvalidSignatureError\n",")\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"XssY97Y9Eahe"},"outputs":[],"source":["'''\n","建置主程序app\n","\n","建置handler與 line_bot_api\n","'''\n","\n","# 設定Server啟用細節\n","app = Flask(__name__,static_url_path = \"/material\" , static_folder = \"./material/\")\n","\n","\n","# 生成實體物件\n","line_bot_api = LineBotApi(\"Channel_Access_Token\")\n","handler = WebhookHandler(\"Channel_Secret\")"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"jyIFMBi4EvTl"},"outputs":[],"source":["'''\n","建置主程序的API入口\n"," 接受Line傳過來的消息\n"," 並取出消息內容\n"," 將消息內容存在google drive的檔案內\n"," 並請handler 進行消息驗證與轉發\n","'''\n","\n","# 啟動server對外接口,使Line能丟消息進來\n","@app.route(\"/\", methods=['POST'])\n","def callback():\n"," # get X-Line-Signature header value\n"," signature = request.headers['X-Line-Signature']\n","\n"," # get request body as text\n"," body = request.get_data(as_text=True)\n"," print(body)\n","\n"," # 記錄用戶log\n"," f = open(\"/content/drive/MyDrive/ai-event.log\", \"a\")\n"," f.write(body)\n"," f.close()\n","\n"," # handle webhook body\n"," try:\n"," handler.handle(body, signature)\n"," except InvalidSignatureError:\n"," abort(400)\n","\n"," return 'OK'"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"UD_P6AenKJ4W"},"outputs":[],"source":["'''\n","\n","消息素材準備\n","\n","'''\n","\n","# 將消息模型,文字收取消息與文字寄發消息 引入\n","from linebot.models import (\n"," MessageEvent, TextMessage, TextSendMessage, ImageSendMessage\n",")\n","\n","specific_text_message=TextSendMessage(\"官方建議,用戶以文字消息觸發特殊功能的時候,要加上特殊符號\")\n","\n","analyze_truth_image_message=ImageSendMessage(\n"," original_content_url='https://i.imgur.com/M8jnn9B.png',\n"," preview_image_url='https://i.imgur.com/LtCtgg0.jpg'\n"," )"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"CsDjhbugRh9Z"},"outputs":[],"source":["'''\n","\n","設計一個字典\n"," 當用戶輸入相應文字消息時,系統會從此挑揀消息\n","\n","'''\n","\n","# 根據自定義菜單四張故事線的圖,設定相對應image\n","template_message_dict = {\n"," \"@123\":specific_text_message,\n"," \"@分析的本質\": analyze_truth_image_message\n","}"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"gUFnh86kQSzk"},"outputs":[],"source":["# 用戶發出文字消息時, 按條件內容, 回傳文字消息\n","@handler.add(MessageEvent, message=TextMessage)\n","def handle_message(event):\n"," \n"," if(event.message.text.find('@')!= -1):\n"," line_bot_api.reply_message(\n"," event.reply_token,\n"," template_message_dict.get(event.message.text)\n"," )\n"," else:\n"," line_bot_api.reply_message(\n"," event.reply_token,\n"," TextSendMessage(text=\"字典內查無此字,請輸入@123或@分析的本質\")\n"," )"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"NVkYb65fHY9W"},"outputs":[],"source":["# 主程序運行\n","port = 5000\n","# Open a ngrok tunnel to the HTTP server\n","public_url = ngrok.connect(port).public_url\n","print(\" * ngrok tunnel \\\"{}\\\" -> \\\"http://127.0.0.1:{}\\\"\".format(public_url, port))\n","app.run()"]}],"metadata":{"colab":{"collapsed_sections":[],"name":"005_LineChatbot基本組成_文字消息的回覆.ipynb","provenance":[]},"kernelspec":{"display_name":"Python 3","name":"python3"}},"nbformat":4,"nbformat_minor":0} 2 | -------------------------------------------------------------------------------- /006_自主練習.ipynb: -------------------------------------------------------------------------------- 1 | {"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"006_自主練習.ipynb","provenance":[],"authorship_tag":"ABX9TyMCo8TgVDXQP/rg9vvjTEj2"},"kernelspec":{"name":"python3","display_name":"Python 3"}},"cells":[{"cell_type":"code","metadata":{"id":"tTxCW435ORit"},"source":[""],"execution_count":null,"outputs":[]}]} -------------------------------------------------------------------------------- /007_LineChatbot基本組成_範本訊息.ipynb: -------------------------------------------------------------------------------- 1 | {"cells":[{"cell_type":"code","execution_count":null,"metadata":{"id":"ylkDvkEfEZIw"},"outputs":[],"source":["'''\n","掛載Google硬碟\n","安裝套件\n","引用套件\n","APP應用準備\n","消息準備\n","handler執行方法設計\n","啟動應用\n","\n","'''"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"2mDqhB78Fxp6"},"outputs":[],"source":["'''\n","\n","資料 mapping 至google drive\n","\n","把資料寫在/content/drive\n","\n","即可保存在 google drive內\n","\n","'''\n","from google.colab import drive\n","drive.mount('/content/drive')"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"4iGRs1oYD6Ca"},"outputs":[],"source":["'''\n","\n","流程解析\n","\n","'''\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"JyhJqq8KImyR"},"outputs":[],"source":["!pip install line-bot-sdk flask \n","\n","!pip install pyngrok"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"rJDNE2TYEaYA"},"outputs":[],"source":["'''\n","引用套件\n","'''\n","\n","# 引用Web Server套件\n","from flask import Flask, request, abort, jsonify\n","\n","# 載入json處理套件\n","import json\n","\n","# 外部連結自動生成套件\n","# from flask_ngrok import run_with_ngrok\n","from pyngrok import ngrok\n","\n","# 從linebot 套件包裡引用 LineBotApi 與 WebhookHandler 類別\n","from linebot import (\n"," LineBotApi, WebhookHandler\n",")\n","\n","# 引用無效簽章錯誤\n","from linebot.exceptions import (\n"," InvalidSignatureError\n",")\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"XssY97Y9Eahe"},"outputs":[],"source":["'''\n","建置主程序\n","\n","建置handler與 line_bot_api\n","'''\n","\n","# 設定Server啟用細節\n","app = Flask(__name__,static_url_path = \"/material\" , static_folder = \"./material/\")\n","\n","\n","# 生成實體物件\n","line_bot_api = LineBotApi(\"Channel_Access_Token\")\n","handler = WebhookHandler(\"Channel_Secret\")"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"jyIFMBi4EvTl"},"outputs":[],"source":["'''\n","建置主程序的API入口\n"," 接受Line傳過來的消息\n"," 並取出消息內容\n"," 將消息內容存在google drive的檔案內\n"," 並請handler 進行消息驗證與轉發\n","'''\n","\n","# 啟動server對外接口,使Line能丟消息進來\n","@app.route(\"/\", methods=['POST'])\n","def callback():\n"," # get X-Line-Signature header value\n"," signature = request.headers['X-Line-Signature']\n","\n"," # get request body as text\n"," body = request.get_data(as_text=True)\n"," print(body)\n","\n"," # 記錄用戶log\n"," f = open(\"/content/drive/MyDrive/ai-event.log\", \"a\")\n"," f.write(body)\n"," f.close()\n","\n"," # handle webhook body\n"," try:\n"," handler.handle(body, signature)\n"," except InvalidSignatureError:\n"," abort(400)\n","\n"," return 'OK'"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"kpVdUJ0wTSli"},"outputs":[],"source":["'''\n","Button篇\n"," 設定模板消息,指定其參數細節。\n","\n","'''\n","\n","\n","#引入所需要的消息與模板消息\n","from linebot.models import (\n"," MessageEvent, TemplateSendMessage , PostbackEvent\n",")\n","\n","#引入按鍵模板\n","from linebot.models.template import(\n"," ButtonsTemplate\n",")\n","\n","\n","'''\n","alt_text: Line簡覽視窗所出現的說明文字\n","template: 所使用的模板\n","ButtonsTemplate: 按鍵模板\n"," thumbnail_image_url: 展示圖片\n"," title: 標題\n"," text: 說明文字\n"," actions: 模板行為所使用的行為\n"," data: 觸發postback後用戶回傳值,可以對其做商業邏輯處理\n","\n","'''\n","buttons_template_message = TemplateSendMessage(\n"," alt_text='Buttons template',\n"," template=ButtonsTemplate(\n"," title='更多幫助',\n"," text='請點擊下方按鈕獲得更多幫助',\n"," actions=[\n"," {\n"," \"type\": \"postback\",\n"," \"label\": \"企業,查找商業結合方案\",\n"," \"text\": \"[::text:]尋找BD\",\n"," \"data\": \"Data1\"\n"," },\n"," {\n"," \"type\": \"postback\",\n"," \"label\": \"開發者,尋求教學\",\n"," \"text\": \"[::text:]求助專家\",\n"," \"data\": \"Data2\"\n"," }\n"," ],\n"," )\n",")"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"CsDjhbugRh9Z"},"outputs":[],"source":["'''\n","\n","設計一個字典\n"," 當用戶輸入相應文字消息時,系統會從此挑揀消息\n","\n","'''\n","\n","# 將消息模型,文字收取消息與文字寄發消息 引入\n","from linebot.models import (\n"," MessageEvent, TextMessage, TextSendMessage, ImageSendMessage\n",")\n","\n","# 根據自定義菜單四張故事線的圖,設定相對應image\n","template_message_dict = {\n"," \"@more\":buttons_template_message,\n","}"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"gUFnh86kQSzk"},"outputs":[],"source":["# 用戶發出文字消息時, 按條件內容, 回傳文字消息\n","@handler.add(MessageEvent, message=TextMessage)\n","def handle_message(event):\n"," \n"," if(event.message.text.find('@')!= -1):\n"," line_bot_api.reply_message(\n"," event.reply_token,\n"," template_message_dict.get(event.message.text)\n"," )\n"," else:\n"," line_bot_api.reply_message(\n"," event.reply_token,\n"," TextSendMessage(text=\"字典內查無此字,請輸入@more\")\n"," )"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"dNFhpNPPTywd"},"outputs":[],"source":["#用戶點擊button後,觸發postback event,對其回傳做相對應處理\n","\n","@handler.add(PostbackEvent)\n","def handle_post_message(event):\n"," user_profile = line_bot_api.get_profile(event.source.user_id)\n"," if (event.postback.data.find('Data1')== 0):\n"," with open(\"user_profile_business.txt\", \"a\") as myfile:\n"," myfile.write(json.dumps(vars(user_profile),sort_keys=True))\n"," myfile.write('\\n')\n"," line_bot_api.reply_message(\n"," event.reply_token,\n"," TextMessage(\n"," text='請稍待,會有專人與您聯繫'\n"," )\n"," )\n"," elif (event.postback.data.find('Data2') == 0):\n"," with open(\"user_profile_tutorial.txt\", \"a\") as myfile:\n"," myfile.write(json.dumps(vars(user_profile),sort_keys=True))\n"," myfile.write('\\n')\n"," line_bot_api.reply_message(\n"," event.reply_token,\n"," TextMessage(\n"," text='請稍待,我們會派專家與您聯繫'\n"," )\n"," )\n"," else:\n"," pass"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"NVkYb65fHY9W"},"outputs":[],"source":["# 主程序運行\n","port = 5000\n","# Open a ngrok tunnel to the HTTP server\n","public_url = ngrok.connect(port).public_url\n","print(\" * ngrok tunnel \\\"{}\\\" -> \\\"http://127.0.0.1:{}\\\"\".format(public_url, port))\n","app.run()"]}],"metadata":{"colab":{"authorship_tag":"ABX9TyPS/X1nIJhu4WEgLEOreLKn","collapsed_sections":[],"mount_file_id":"1_Qz4yVKsgaS6lF2vvbz1_nIzNdg4zA4v","name":"007_LineChatbot基本組成_範本訊息.ipynb","provenance":[]},"kernelspec":{"display_name":"Python 3","name":"python3"}},"nbformat":4,"nbformat_minor":0} 2 | -------------------------------------------------------------------------------- /008_自主練習.ipynb: -------------------------------------------------------------------------------- 1 | {"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"008_自主練習.ipynb","provenance":[],"authorship_tag":"ABX9TyMCo8TgVDXQP/rg9vvjTEj2"},"kernelspec":{"name":"python3","display_name":"Python 3"}},"cells":[{"cell_type":"code","metadata":{"id":"lq5PQA7FPPIi"},"source":[""],"execution_count":null,"outputs":[]}]} -------------------------------------------------------------------------------- /009_LineChatbot基本組成_範本訊息的缺點.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": { 7 | "id": "ylkDvkEfEZIw" 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "'''\n", 12 | "掛載Google硬碟\n", 13 | "安裝套件\n", 14 | "引用套件\n", 15 | "APP應用準備\n", 16 | "消息準備\n", 17 | "handler執行方法設計\n", 18 | "啟動應用\n", 19 | "\n", 20 | "'''" 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": null, 26 | "metadata": { 27 | "id": "2mDqhB78Fxp6" 28 | }, 29 | "outputs": [], 30 | "source": [ 31 | "'''\n", 32 | "\n", 33 | "資料 mapping 至google drive\n", 34 | "\n", 35 | "把資料寫在/content/drive\n", 36 | "\n", 37 | "即可保存在 google drive內\n", 38 | "\n", 39 | "'''\n", 40 | "from google.colab import drive\n", 41 | "drive.mount('/content/drive')" 42 | ] 43 | }, 44 | { 45 | "cell_type": "code", 46 | "execution_count": null, 47 | "metadata": { 48 | "id": "4iGRs1oYD6Ca" 49 | }, 50 | "outputs": [], 51 | "source": [ 52 | "'''\n", 53 | "\n", 54 | "流程解析\n", 55 | "\n", 56 | "'''\n" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": null, 62 | "metadata": { 63 | "id": "JyhJqq8KImyR" 64 | }, 65 | "outputs": [], 66 | "source": [ 67 | "!pip install line-bot-sdk flask \n", 68 | "\n", 69 | "!pip install pyngrok" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": null, 75 | "metadata": { 76 | "id": "rJDNE2TYEaYA" 77 | }, 78 | "outputs": [], 79 | "source": [ 80 | "'''\n", 81 | "引用套件\n", 82 | "'''\n", 83 | "\n", 84 | "# 引用Web Server套件\n", 85 | "from flask import Flask, request, abort, jsonify\n", 86 | "\n", 87 | "# 載入json處理套件\n", 88 | "import json\n", 89 | "\n", 90 | "# 外部連結自動生成套件\n", 91 | "# from flask_ngrok import run_with_ngrok\n", 92 | "from pyngrok import ngrok\n", 93 | "\n", 94 | "# 從linebot 套件包裡引用 LineBotApi 與 WebhookHandler 類別\n", 95 | "from linebot import (\n", 96 | " LineBotApi, WebhookHandler\n", 97 | ")\n", 98 | "\n", 99 | "# 引用無效簽章錯誤\n", 100 | "from linebot.exceptions import (\n", 101 | " InvalidSignatureError\n", 102 | ")\n" 103 | ] 104 | }, 105 | { 106 | "cell_type": "code", 107 | "execution_count": null, 108 | "metadata": { 109 | "id": "XssY97Y9Eahe" 110 | }, 111 | "outputs": [], 112 | "source": [ 113 | "'''\n", 114 | "建置主程序\n", 115 | "\n", 116 | "建置handler與 line_bot_api\n", 117 | "'''\n", 118 | "\n", 119 | "# 設定Server啟用細節\n", 120 | "app = Flask(__name__,static_url_path = \"/material\" , static_folder = \"./material/\")\n", 121 | "\n", 122 | "# 生成實體物件\n", 123 | "line_bot_api = LineBotApi(\"CHANNEL_ACCESS_TOKEN\")\n", 124 | "handler = WebhookHandler(\"CHANNEL_SECRET\")" 125 | ] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "execution_count": null, 130 | "metadata": { 131 | "id": "jyIFMBi4EvTl" 132 | }, 133 | "outputs": [], 134 | "source": [ 135 | "'''\n", 136 | "建置主程序的API入口\n", 137 | " 接受Line傳過來的消息\n", 138 | " 並取出消息內容\n", 139 | " 將消息內容存在google drive的檔案內\n", 140 | " 並請handler 進行消息驗證與轉發\n", 141 | "'''\n", 142 | "\n", 143 | "\n", 144 | "# 啟動server對外接口,使Line能丟消息進來\n", 145 | "@app.route(\"/\", methods=['POST'])\n", 146 | "def callback():\n", 147 | " # get X-Line-Signature header value\n", 148 | " signature = request.headers['X-Line-Signature']\n", 149 | "\n", 150 | " # get request body as text\n", 151 | " body = request.get_data(as_text=True)\n", 152 | " print(body)\n", 153 | "\n", 154 | " # 記錄用戶log\n", 155 | " f = open(\"/content/drive/MyDrive/ai-event.log\", \"a\")\n", 156 | " f.write(body)\n", 157 | " f.close()\n", 158 | "\n", 159 | " # handle webhook body\n", 160 | " try:\n", 161 | " handler.handle(body, signature)\n", 162 | " except InvalidSignatureError:\n", 163 | " abort(400)\n", 164 | "\n", 165 | " return 'OK'" 166 | ] 167 | }, 168 | { 169 | "cell_type": "code", 170 | "execution_count": null, 171 | "metadata": { 172 | "id": "CLJukg__Yaal" 173 | }, 174 | "outputs": [], 175 | "source": [ 176 | "'''\n", 177 | "\n", 178 | "準備QuickReply的Button\n", 179 | "\n", 180 | "\n", 181 | "'''\n", 182 | "\n", 183 | "# 引入相關套件\n", 184 | "from linebot.models import (\n", 185 | " MessageAction, URIAction,\n", 186 | " PostbackAction, DatetimePickerAction,\n", 187 | " CameraAction, CameraRollAction, LocationAction,\n", 188 | " QuickReply, QuickReplyButton\n", 189 | ")\n", 190 | "\n", 191 | "# 創建QuickReplyButton \n", 192 | "\n", 193 | "## 點擊後,以用戶身份發送文字消息\n", 194 | "## MessageAction\n", 195 | "textQuickReplyButton = QuickReplyButton(\n", 196 | " action=MessageAction(\n", 197 | " label=\"發送文字消息\", \n", 198 | " text=\"text2\"\n", 199 | " )\n", 200 | ")\n", 201 | "\n", 202 | "## 點擊後,彈跳出選擇時間之視窗\n", 203 | "## DatetimePickerAction\n", 204 | "dateQuickReplyButton = QuickReplyButton(\n", 205 | " action=DatetimePickerAction(\n", 206 | " label=\"時間選擇\",\n", 207 | " data=\"data3\", \n", 208 | " mode=\"date\"\n", 209 | " )\n", 210 | ")\n", 211 | "\n", 212 | "\n", 213 | "## 點擊後,開啟相機\n", 214 | "## CameraAction\n", 215 | "cameraQuickReplyButton = QuickReplyButton(\n", 216 | " action=CameraAction(label=\"拍照\")\n", 217 | ")\n", 218 | "\n", 219 | "## 點擊後,切換至照片相簿選擇\n", 220 | "cameraRollQRB = QuickReplyButton(\n", 221 | " action=CameraRollAction(label=\"選擇照片\")\n", 222 | ")\n", 223 | "\n", 224 | "## 點擊後,跳出地理位置\n", 225 | "locationQRB = QuickReplyButton(\n", 226 | " action=LocationAction(label=\"地理位置\")\n", 227 | ")\n", 228 | "\n", 229 | "## 點擊後,以Postback事件回應Server \n", 230 | "postbackQRB = QuickReplyButton(\n", 231 | " action=PostbackAction(label=\"我是PostbackEvent\", data=\"Data1\")\n", 232 | ")" 233 | ] 234 | }, 235 | { 236 | "cell_type": "code", 237 | "execution_count": null, 238 | "metadata": { 239 | "id": "wIN_RgjHYiKe" 240 | }, 241 | "outputs": [], 242 | "source": [ 243 | "'''\n", 244 | "\n", 245 | "以QuickReply封裝該些QuickReply Button\n", 246 | "\n", 247 | "'''\n", 248 | "## 設計QuickReplyButton的List\n", 249 | "quickReplyList = QuickReply(\n", 250 | " items = [textQuickReplyButton, dateQuickReplyButton, cameraQuickReplyButton, cameraRollQRB, locationQRB,postbackQRB]\n", 251 | ")" 252 | ] 253 | }, 254 | { 255 | "cell_type": "code", 256 | "execution_count": null, 257 | "metadata": { 258 | "id": "QGQAcwWoYlb0" 259 | }, 260 | "outputs": [], 261 | "source": [ 262 | "'''\n", 263 | "\n", 264 | "製作TextSendMessage,並將剛封裝的QuickReply放入\n", 265 | "\n", 266 | "'''\n", 267 | "## 將quickReplyList 塞入TextSendMessage 中 \n", 268 | "from linebot.models import (\n", 269 | " TextSendMessage,\n", 270 | ")\n", 271 | "quick_reply_text_send_message = TextSendMessage(text='發送問題給用戶,請用戶回答', quick_reply=quickReplyList)" 272 | ] 273 | }, 274 | { 275 | "cell_type": "code", 276 | "execution_count": null, 277 | "metadata": { 278 | "id": "CsDjhbugRh9Z" 279 | }, 280 | "outputs": [], 281 | "source": [ 282 | "'''\n", 283 | "\n", 284 | "設計一個字典\n", 285 | " 當用戶輸入相應文字消息時,系統會從此挑揀消息\n", 286 | "\n", 287 | "'''\n", 288 | "\n", 289 | "# 將消息模型,文字收取消息與文字寄發消息 引入\n", 290 | "from linebot.models import (\n", 291 | " MessageEvent, TextMessage, TextSendMessage, ImageSendMessage\n", 292 | ")\n", 293 | "\n", 294 | "# 根據自定義菜單四張故事線的圖,設定相對應image\n", 295 | "template_message_dict = {\n", 296 | " \"@reply\":quick_reply_text_send_message,\n", 297 | "}" 298 | ] 299 | }, 300 | { 301 | "cell_type": "code", 302 | "execution_count": null, 303 | "metadata": { 304 | "id": "dNFhpNPPTywd" 305 | }, 306 | "outputs": [], 307 | "source": [ 308 | "#用戶點擊button後,觸發postback event,對其回傳做相對應處理\n", 309 | "from linebot.models import PostbackEvent\n", 310 | "\n", 311 | "@handler.add(PostbackEvent)\n", 312 | "def handle_post_message(event):\n", 313 | " user_profile = line_bot_api.get_profile(event.source.user_id)\n", 314 | " if (event.postback.data.find('Data1')== 0):\n", 315 | " with open(\"user_profile_business.txt\", \"a\") as myfile:\n", 316 | " myfile.write(json.dumps(vars(user_profile),sort_keys=True))\n", 317 | " myfile.write('\\n')\n", 318 | " line_bot_api.reply_message(\n", 319 | " event.reply_token,\n", 320 | " TextSendMessage(\n", 321 | " text='請稍待,會有專人與您聯繫'\n", 322 | " )\n", 323 | " )\n", 324 | " elif (event.postback.data.find('Data2') == 0):\n", 325 | " with open(\"user_profile_tutorial.txt\", \"a\") as myfile:\n", 326 | " myfile.write(json.dumps(vars(user_profile),sort_keys=True))\n", 327 | " myfile.write('\\n')\n", 328 | " line_bot_api.reply_message(\n", 329 | " event.reply_token,\n", 330 | " TextSendMessage(\n", 331 | " text='請稍待,我們會派專家您聯繫'\n", 332 | " )\n", 333 | " )\n", 334 | " else:\n", 335 | " pass" 336 | ] 337 | }, 338 | { 339 | "cell_type": "code", 340 | "execution_count": null, 341 | "metadata": { 342 | "id": "gUFnh86kQSzk" 343 | }, 344 | "outputs": [], 345 | "source": [ 346 | "# 用戶發出文字消息時, 按條件內容, 回傳文字消息\n", 347 | "@handler.add(MessageEvent, message=TextMessage)\n", 348 | "def handle_message(event):\n", 349 | " \n", 350 | " if(event.message.text.find('@')!= -1):\n", 351 | " line_bot_api.reply_message(\n", 352 | " event.reply_token,\n", 353 | " template_message_dict.get(event.message.text)\n", 354 | " )\n", 355 | " else:\n", 356 | " line_bot_api.reply_message(\n", 357 | " event.reply_token,\n", 358 | " TextSendMessage(text=\"字典內查無此字,請輸入@more\")\n", 359 | " )" 360 | ] 361 | }, 362 | { 363 | "cell_type": "code", 364 | "execution_count": null, 365 | "metadata": { 366 | "id": "NVkYb65fHY9W" 367 | }, 368 | "outputs": [], 369 | "source": [ 370 | "# 主程序運行\n", 371 | "port = 5000\n", 372 | "# Open a ngrok tunnel to the HTTP server\n", 373 | "public_url = ngrok.connect(port).public_url\n", 374 | "print(\" * ngrok tunnel \\\"{}\\\" -> \\\"http://127.0.0.1:{}\\\"\".format(public_url, port))\n", 375 | "app.run()" 376 | ] 377 | } 378 | ], 379 | "metadata": { 380 | "colab": { 381 | "collapsed_sections": [], 382 | "name": "009_LineChatbot基本組成_範本訊息的缺點.ipynb", 383 | "provenance": [] 384 | }, 385 | "kernelspec": { 386 | "display_name": "Python 3", 387 | "language": "python", 388 | "name": "python3" 389 | }, 390 | "language_info": { 391 | "codemirror_mode": { 392 | "name": "ipython", 393 | "version": 3 394 | }, 395 | "file_extension": ".py", 396 | "mimetype": "text/x-python", 397 | "name": "python", 398 | "nbconvert_exporter": "python", 399 | "pygments_lexer": "ipython3", 400 | "version": "3.8.6" 401 | } 402 | }, 403 | "nbformat": 4, 404 | "nbformat_minor": 1 405 | } 406 | -------------------------------------------------------------------------------- /010_自主練習.ipynb: -------------------------------------------------------------------------------- 1 | {"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"010_自主練習.ipynb","provenance":[],"authorship_tag":"ABX9TyMCo8TgVDXQP/rg9vvjTEj2"},"kernelspec":{"name":"python3","display_name":"Python 3"}},"cells":[{"cell_type":"code","metadata":{"id":"LI0SoqfUPWz8"},"source":[""],"execution_count":null,"outputs":[]}]} -------------------------------------------------------------------------------- /011_LineChatbot基本組成_下載多媒體訊息.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": { 7 | "id": "ylkDvkEfEZIw" 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "'''\n", 12 | "掛載Google硬碟\n", 13 | "安裝套件\n", 14 | "引用套件\n", 15 | "APP應用準備\n", 16 | "消息準備\n", 17 | "handler執行方法設計\n", 18 | "啟動應用\n", 19 | "\n", 20 | "'''" 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": null, 26 | "metadata": { 27 | "id": "2mDqhB78Fxp6" 28 | }, 29 | "outputs": [], 30 | "source": [ 31 | "'''\n", 32 | "\n", 33 | "資料 mapping 至google drive\n", 34 | "\n", 35 | "把資料寫在/content/drive\n", 36 | "\n", 37 | "即可保存在 google drive內\n", 38 | "\n", 39 | "'''\n", 40 | "from google.colab import drive\n", 41 | "drive.mount('/content/drive')" 42 | ] 43 | }, 44 | { 45 | "cell_type": "code", 46 | "execution_count": null, 47 | "metadata": { 48 | "id": "4iGRs1oYD6Ca" 49 | }, 50 | "outputs": [], 51 | "source": [ 52 | "'''\n", 53 | "\n", 54 | "流程解析\n", 55 | "\n", 56 | "'''\n" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": null, 62 | "metadata": { 63 | "id": "JyhJqq8KImyR" 64 | }, 65 | "outputs": [], 66 | "source": [ 67 | "!pip install line-bot-sdk flask \n", 68 | "!pip install pyngrok" 69 | ] 70 | }, 71 | { 72 | "cell_type": "code", 73 | "execution_count": null, 74 | "metadata": { 75 | "id": "rJDNE2TYEaYA" 76 | }, 77 | "outputs": [], 78 | "source": [ 79 | "'''\n", 80 | "引用套件\n", 81 | "'''\n", 82 | "\n", 83 | "# 引用Web Server套件\n", 84 | "from flask import Flask, request, abort, jsonify\n", 85 | "\n", 86 | "# 載入json處理套件\n", 87 | "import json\n", 88 | "\n", 89 | "# 外部連結自動生成套件\n", 90 | "# from flask_ngrok import run_with_ngrok\n", 91 | "from pyngrok import ngrok\n", 92 | "\n", 93 | "# 從linebot 套件包裡引用 LineBotApi 與 WebhookHandler 類別\n", 94 | "from linebot import (\n", 95 | " LineBotApi, WebhookHandler\n", 96 | ")\n", 97 | "\n", 98 | "# 引用無效簽章錯誤\n", 99 | "from linebot.exceptions import (\n", 100 | " InvalidSignatureError\n", 101 | ")\n" 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": null, 107 | "metadata": { 108 | "id": "XssY97Y9Eahe" 109 | }, 110 | "outputs": [], 111 | "source": [ 112 | "'''\n", 113 | "建置主程序\n", 114 | "\n", 115 | "建置handler與 line_bot_api\n", 116 | "'''\n", 117 | "\n", 118 | "# 設定Server啟用細節\n", 119 | "app = Flask(__name__,static_url_path = \"/material\" , static_folder = \"./material/\")\n", 120 | "\n", 121 | "\n", 122 | "# 生成實體物件\n", 123 | "line_bot_api = LineBotApi(\"CHANNEL_ACCESS_TOKEN\")\n", 124 | "handler = WebhookHandler(\"CHANNEL_SECRET\")" 125 | ] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "execution_count": null, 130 | "metadata": { 131 | "id": "jyIFMBi4EvTl" 132 | }, 133 | "outputs": [], 134 | "source": [ 135 | "'''\n", 136 | "建置主程序的API入口\n", 137 | " 接受Line傳過來的消息\n", 138 | " 並取出消息內容\n", 139 | " 將消息內容存在google drive的檔案內\n", 140 | " 並請handler 進行消息驗證與轉發\n", 141 | "'''\n", 142 | "\n", 143 | "# 啟動server對外接口,使Line能丟消息進來\n", 144 | "@app.route(\"/\", methods=['POST'])\n", 145 | "def callback():\n", 146 | " # get X-Line-Signature header value\n", 147 | " signature = request.headers['X-Line-Signature']\n", 148 | "\n", 149 | " # get request body as text\n", 150 | " body = request.get_data(as_text=True)\n", 151 | " print(body)\n", 152 | "\n", 153 | " # 記錄用戶log\n", 154 | " f = open(\"/content/drive/MyDrive/ai-event.log\", \"a\")\n", 155 | " f.write(body)\n", 156 | " f.close()\n", 157 | "\n", 158 | " # handle webhook body\n", 159 | " try:\n", 160 | " handler.handle(body, signature)\n", 161 | " except InvalidSignatureError:\n", 162 | " abort(400)\n", 163 | "\n", 164 | " return 'OK'" 165 | ] 166 | }, 167 | { 168 | "cell_type": "code", 169 | "execution_count": null, 170 | "metadata": { 171 | "id": "jojZfXmcacqS" 172 | }, 173 | "outputs": [], 174 | "source": [ 175 | "'''\n", 176 | "\n", 177 | "若收到圖片消息時,\n", 178 | "\n", 179 | "先回覆用戶文字消息,並從Line上將照片拿回。\n", 180 | "\n", 181 | "'''\n", 182 | "\n", 183 | "from linebot.models import(\n", 184 | " MessageEvent,ImageMessage, TextSendMessage\n", 185 | ")\n", 186 | "\n", 187 | "@handler.add(MessageEvent, message=ImageMessage)\n", 188 | "def handle_message(event):\n", 189 | " line_bot_api.reply_message(\n", 190 | " event.reply_token,\n", 191 | " TextSendMessage(text='Image has Upload'+ ' ' + event.message.id))\n", 192 | " message_content = line_bot_api.get_message_content(event.message.id)\n", 193 | " with open(event.message.id+'.jpg', 'wb') as fd:\n", 194 | " for chunk in message_content.iter_content():\n", 195 | " fd.write(chunk)" 196 | ] 197 | }, 198 | { 199 | "cell_type": "code", 200 | "execution_count": null, 201 | "metadata": { 202 | "id": "NVkYb65fHY9W" 203 | }, 204 | "outputs": [], 205 | "source": [ 206 | "'''\n", 207 | "影片消息,留給你\n", 208 | "'''" 209 | ] 210 | }, 211 | { 212 | "cell_type": "code", 213 | "execution_count": null, 214 | "metadata": { 215 | "id": "H7hA7xfaQ25T" 216 | }, 217 | "outputs": [], 218 | "source": [ 219 | "'''\n", 220 | "音訊消息,留給你\n", 221 | "'''" 222 | ] 223 | }, 224 | { 225 | "cell_type": "code", 226 | "execution_count": null, 227 | "metadata": { 228 | "id": "0IONhIKOQ3Ih" 229 | }, 230 | "outputs": [], 231 | "source": [] 232 | }, 233 | { 234 | "cell_type": "code", 235 | "execution_count": null, 236 | "metadata": { 237 | "id": "7UVshk_cQ3NQ" 238 | }, 239 | "outputs": [], 240 | "source": [ 241 | "# 主程序運行\n", 242 | "port = 5000\n", 243 | "# Open a ngrok tunnel to the HTTP server\n", 244 | "public_url = ngrok.connect(port).public_url\n", 245 | "print(\" * ngrok tunnel \\\"{}\\\" -> \\\"http://127.0.0.1:{}\\\"\".format(public_url, port))\n", 246 | "app.run()" 247 | ] 248 | } 249 | ], 250 | "metadata": { 251 | "colab": { 252 | "collapsed_sections": [], 253 | "name": "011_LineChatbot基本組成_下載多媒體訊息.ipynb", 254 | "provenance": [] 255 | }, 256 | "kernelspec": { 257 | "display_name": "Python 3", 258 | "language": "python", 259 | "name": "python3" 260 | }, 261 | "language_info": { 262 | "codemirror_mode": { 263 | "name": "ipython", 264 | "version": 3 265 | }, 266 | "file_extension": ".py", 267 | "mimetype": "text/x-python", 268 | "name": "python", 269 | "nbconvert_exporter": "python", 270 | "pygments_lexer": "ipython3", 271 | "version": "3.8.6" 272 | } 273 | }, 274 | "nbformat": 4, 275 | "nbformat_minor": 1 276 | } 277 | -------------------------------------------------------------------------------- /011_自主練習.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": { 7 | "id": "LI0SoqfUPWz8" 8 | }, 9 | "outputs": [], 10 | "source": [] 11 | } 12 | ], 13 | "metadata": { 14 | "colab": { 15 | "authorship_tag": "ABX9TyMCo8TgVDXQP/rg9vvjTEj2", 16 | "name": "010_自主練習.ipynb", 17 | "provenance": [] 18 | }, 19 | "kernelspec": { 20 | "display_name": "Python 3", 21 | "name": "python3" 22 | } 23 | }, 24 | "nbformat": 4, 25 | "nbformat_minor": 0 26 | } 27 | -------------------------------------------------------------------------------- /012_LineChatbot綜合.ipynb: -------------------------------------------------------------------------------- 1 | {"cells":[{"cell_type":"code","execution_count":null,"metadata":{"id":"ylkDvkEfEZIw"},"outputs":[],"source":["'''\n","掛載Google硬碟\n","安裝套件\n","引用套件\n","APP應用準備\n","消息準備\n","handler執行方法設計\n","啟動應用\n","\n","'''"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"2mDqhB78Fxp6"},"outputs":[],"source":["'''\n","\n","資料 mapping 至google drive\n","\n","把資料寫在/content/drive\n","\n","即可保存在 google drive內\n","\n","'''\n","from google.colab import drive\n","drive.mount('/content/drive')"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"4iGRs1oYD6Ca"},"outputs":[],"source":["'''\n","\n","流程解析\n","\n","'''\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"JyhJqq8KImyR"},"outputs":[],"source":["!pip install line-bot-sdk flask \n","!pip install pyngrok"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"rJDNE2TYEaYA"},"outputs":[],"source":["'''\n","\n","\n","'''\n","\n","# 引用Web Server套件\n","from flask import Flask, request, abort, jsonify\n","\n","# 載入json處理套件\n","import json\n","\n","# 外部連結自動生成套件\n","# from flask_ngrok import run_with_ngrok\n","from pyngrok import ngrok\n","\n","# 從linebot 套件包裡引用 LineBotApi 與 WebhookHandler 類別\n","from linebot import (\n"," LineBotApi, WebhookHandler\n",")\n","\n","# 引用無效簽章錯誤\n","from linebot.exceptions import (\n"," InvalidSignatureError\n",")\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"XssY97Y9Eahe"},"outputs":[],"source":["'''\n","建置主程序\n","\n","建置handler與 line_bot_api\n","'''\n","\n","# 設定Server啟用細節\n","app = Flask(__name__,static_url_path = \"/material\" , static_folder = \"./material/\")\n","\n","\n","# 生成實體物件\n","line_bot_api = LineBotApi(\"CHANNEL_ACCESS_TOKEN\")\n","handler = WebhookHandler(\"CHANNEL_SECRET\")"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"jyIFMBi4EvTl"},"outputs":[],"source":["'''\n","\n","建置主程序的API入口\n","\n","'''\n","\n","# 啟動server對外接口,使Line能丟消息進來\n","@app.route(\"/\", methods=['POST'])\n","def callback():\n"," # get X-Line-Signature header value\n"," signature = request.headers['X-Line-Signature']\n","\n"," # get request body as text\n"," body = request.get_data(as_text=True)\n"," print(body)\n","\n"," # 記錄用戶log\n"," f = open(\"/content/drive/MyDrive/ai-event.log\", \"a\")\n"," f.write(body)\n"," f.close()\n","\n"," # handle webhook body\n"," try:\n"," handler.handle(body, signature)\n"," except InvalidSignatureError:\n"," abort(400)\n","\n"," return 'OK'"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"CLJukg__Yaal"},"outputs":[],"source":["'''\n","\n","準備QuickReply的Button\n","\n","\n","'''\n","\n","# 引入相關套件\n","from linebot.models import (\n"," MessageAction, URIAction,\n"," PostbackAction, DatetimePickerAction,\n"," CameraAction, CameraRollAction, LocationAction,\n"," QuickReply, QuickReplyButton\n",")\n","\n","# 創建QuickReplyButton \n","\n","## 點擊後,以用戶身份發送文字消息\n","## MessageAction\n","textQuickReplyButton = QuickReplyButton(\n"," action=MessageAction(\n"," label=\"發送文字消息\", \n"," text=\"text2\"\n"," )\n",")\n","\n","## 點擊後,彈跳出選擇時間之視窗\n","## DatetimePickerAction\n","dateQuickReplyButton = QuickReplyButton(\n"," action=DatetimePickerAction(\n"," label=\"時間選擇\",\n"," data=\"data3\", \n"," mode=\"date\"\n"," )\n",")\n","\n","\n","## 點擊後,開啟相機\n","## CameraAction\n","cameraQuickReplyButton = QuickReplyButton(\n"," action=CameraAction(label=\"拍照\")\n",")\n","\n","## 點擊後,切換至照片相簿選擇\n","cameraRollQRB = QuickReplyButton(\n"," action=CameraRollAction(label=\"選擇照片\")\n",")\n","\n","## 點擊後,跳出地理位置\n","locationQRB = QuickReplyButton(\n"," action=LocationAction(label=\"地理位置\")\n",")\n","\n","## 點擊後,以Postback事件回應Server \n","postbackQRB = QuickReplyButton(\n"," action=PostbackAction(label=\"我是PostbackEvent\", data=\"data1\")\n",")"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"wIN_RgjHYiKe"},"outputs":[],"source":["'''\n","\n","以QuickReply封裝該些QuickReply Button\n","\n","'''\n","## 設計QuickReplyButton的List\n","quickReplyList = QuickReply(\n"," items = [textQuickReplyButton, dateQuickReplyButton, cameraQuickReplyButton, cameraRollQRB, locationQRB,postbackQRB]\n",")"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"QGQAcwWoYlb0"},"outputs":[],"source":["'''\n","\n","製作TextSendMessage,並將剛封裝的QuickReply放入\n","\n","'''\n","## 將quickReplyList 塞入TextSendMessage 中 \n","from linebot.models import (\n"," TextSendMessage,\n",")\n","quick_reply_text_send_message = TextSendMessage(text='發送問題給用戶,請用戶回答', quick_reply=quickReplyList)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"iZf8SHFfXYRs"},"outputs":[],"source":["'''\n","訊息事件的表達\n","'''\n","\n","# 將消息模型,文字收取消息與文字寄發消息 引入\n","from linebot.models import (\n"," MessageEvent, TextMessage, TextSendMessage, ImageSendMessage\n",")\n","\n","specific_text_message=TextSendMessage(\"官方建議,用戶以文字消息觸發特殊功能的時候,要加上特殊符號\")\n","\n","analyze_truth_image_message=ImageSendMessage(\n"," original_content_url='https://i.imgur.com/M8jnn9B.png',\n"," preview_image_url='https://i.imgur.com/LtCtgg0.jpg'\n"," )"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"kpVdUJ0wTSli"},"outputs":[],"source":["'''\n","Button篇\n"," 設定模板消息,指定其參數細節。\n","\n","'''\n","\n","\n","#引入所需要的消息與模板消息\n","from linebot.models import (\n"," MessageEvent, TemplateSendMessage , PostbackEvent\n",")\n","\n","#引入按鍵模板\n","from linebot.models.template import(\n"," ButtonsTemplate\n",")\n","\n","\n","'''\n","alt_text: Line簡覽視窗所出現的說明文字\n","template: 所使用的模板\n","ButtonsTemplate: 按鍵模板\n"," thumbnail_image_url: 展示圖片\n"," title: 標題\n"," text: 說明文字\n"," actions: 模板行為所使用的行為\n"," data: 觸發postback後用戶回傳值,可以對其做商業邏輯處理\n","\n","'''\n","buttons_template_message = TemplateSendMessage(\n"," alt_text='Buttons template',\n"," template=ButtonsTemplate(\n"," title='更多幫助',\n"," text='請點擊下方按鈕獲得更多幫助',\n"," actions=[\n"," {\n"," \"type\": \"postback\",\n"," \"label\": \"企業,查找商業結合方案\",\n"," \"text\": \"[::text:]尋找BD\",\n"," \"data\": \"Data1\"\n"," },\n"," {\n"," \"type\": \"postback\",\n"," \"label\": \"開發者,尋求教學\",\n"," \"text\": \"[::text:]求助專家\",\n"," \"data\": \"Data2\"\n"," }\n"," ],\n"," )\n",")"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"CsDjhbugRh9Z"},"outputs":[],"source":["'''\n","\n","設計一個字典\n"," 當用戶輸入相應文字消息時,系統會從此挑揀消息\n","\n","'''\n","\n","# 將消息模型,文字收取消息與文字寄發消息 引入\n","from linebot.models import (\n"," MessageEvent, TextMessage, TextSendMessage, ImageSendMessage\n",")\n","\n","# 根據自定義菜單四張故事線的圖,設定相對應image\n","template_message_dict = {\n"," \"@reply\":quick_reply_text_send_message,\n"," \"@more\":buttons_template_message,\n"," \"@123\":TextSendMessage(\"官方建議,用戶以文字消息觸發特殊功能的時候,要加上特殊符號\"),\n"," \"@分析的本質\":ImageSendMessage(\n"," original_content_url='https://i.imgur.com/M8jnn9B.png',\n"," preview_image_url='https://i.imgur.com/LtCtgg0.jpg'\n"," )\n","}"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"YhyKB6njFHJC"},"outputs":[],"source":["\n","'''\n","\n","撰寫用戶關注時,我們要處理的商業邏輯\n","\n","1. 取得用戶個資,並存回伺服器\n","2. 回應用戶,歡迎用的文字消息\n","\n","'''\n","\n","\n","# 載入Follow事件\n","from linebot.models.events import (\n"," FollowEvent\n",")\n","\n","from linebot.models import(\n"," TextSendMessage, ImageSendMessage\n",")\n","\n","# 載入requests套件\n","import requests\n","\n","\n","# 告知handler,如果收到FollowEvent,則做下面的方法處理\n","@handler.add(FollowEvent)\n","def reply_text_and_get_user_profile(event):\n"," \n"," # 取出消息內User的資料\n"," user_profile = line_bot_api.get_profile(event.source.user_id)\n"," \n"," # 將用戶資訊存在檔案內\n"," with open(\"/content/drive/MyDrive/users.txt\", \"a\") as myfile:\n"," myfile.write(json.dumps(vars(user_profile),sort_keys=True))\n"," myfile.write('\\n')\n"," \n"," # 回覆文字消息與圖片消息\n"," line_bot_api.reply_message(\n"," event.reply_token,\n"," [TextSendMessage('安安,你的個資已被我紀錄了')]\n"," )"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"dNFhpNPPTywd"},"outputs":[],"source":["#用戶點擊button後,觸發postback event,對其回傳做相對應處理\n","\n","@handler.add(PostbackEvent)\n","def handle_post_message(event):\n"," user_profile = line_bot_api.get_profile(event.source.user_id)\n"," if (event.postback.data.find('Data1')== 0):\n"," with open(\"user_profile_business.txt\", \"a\") as myfile:\n"," myfile.write(json.dumps(vars(user_profile),sort_keys=True))\n"," myfile.write('\\n')\n"," line_bot_api.reply_message(\n"," event.reply_token,\n"," TextMessage(\n"," text='請稍待,會有專人與您聯繫'\n"," )\n"," )\n"," elif (event.postback.data.find('Data2') == 0):\n"," with open(\"user_profile_tutorial.txt\", \"a\") as myfile:\n"," myfile.write(json.dumps(vars(user_profile),sort_keys=True))\n"," myfile.write('\\n')\n"," line_bot_api.reply_message(\n"," event.reply_token,\n"," TextMessage(\n"," text='請稍待,我們會派專家與您聯繫'\n"," )\n"," )\n"," else:\n"," pass"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"gUFnh86kQSzk"},"outputs":[],"source":["# 用戶發出文字消息時, 按條件內容, 回傳文字消息\n","@handler.add(MessageEvent, message=TextMessage)\n","def handle_message(event):\n"," \n"," if(event.message.text.find('@')!= -1):\n"," line_bot_api.reply_message(\n"," event.reply_token,\n"," template_message_dict.get(event.message.text)\n"," )\n"," else:\n"," line_bot_api.reply_message(\n"," event.reply_token,\n"," TextSendMessage(text=\"字典內查無此字,請輸入@123或@分析的本質或@more\")\n"," )"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"jojZfXmcacqS"},"outputs":[],"source":["'''\n","\n","若收到圖片消息時,\n","\n","先回覆用戶文字消息,並從Line上將照片拿回。\n","\n","'''\n","\n","from linebot.models import(\n"," MessageEvent,ImageMessage, TextSendMessage\n",")\n","\n","@handler.add(MessageEvent, message=ImageMessage)\n","def handle_message(event):\n"," line_bot_api.reply_message(\n"," event.reply_token,\n"," TextSendMessage(text='Image has Upload'+ ' ' + event.message.id))\n"," message_content = line_bot_api.get_message_content(event.message.id)\n"," with open(event.message.id+'.jpg', 'wb') as fd:\n"," for chunk in message_content.iter_content():\n"," fd.write(chunk)"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"NVkYb65fHY9W","outputId":"7ac49c5a-31f1-49ca-f016-0e94fc751922"},"outputs":[{"name":"stdout","output_type":"stream","text":[" * Serving Flask app \"__main__\" (lazy loading)\n"," * Environment: production\n","\u001b[31m WARNING: This is a development server. Do not use it in a production deployment.\u001b[0m\n","\u001b[2m Use a production WSGI server instead.\u001b[0m\n"," * Debug mode: off\n"]},{"name":"stderr","output_type":"stream","text":[" * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)\n"]},{"name":"stdout","output_type":"stream","text":[" * Running on http://dc46937283e9.ngrok.io\n"," * Traffic stats available on http://127.0.0.1:4040\n","{\"events\":[{\"type\":\"message\",\"replyToken\":\"259d27b440674dea8a420296edc12d54\",\"source\":{\"userId\":\"Ud29c5cf085e845db7c6457677116df4d\",\"type\":\"user\"},\"timestamp\":1606039859529,\"mode\":\"active\",\"message\":{\"type\":\"image\",\"id\":\"13074981716865\",\"contentProvider\":{\"type\":\"line\"}}}],\"destination\":\"Uf3c9a099570ca0111762abd3f35b81e4\"}\n"]},{"name":"stderr","output_type":"stream","text":["127.0.0.1 - - [22/Nov/2020 10:11:03] \"\u001b[37mPOST / HTTP/1.1\u001b[0m\" 200 -\n"]}],"source":["# 主程序運行\n","port = 5000\n","# Open a ngrok tunnel to the HTTP server\n","public_url = ngrok.connect(port).public_url\n","print(\" * ngrok tunnel \\\"{}\\\" -> \\\"http://127.0.0.1:{}\\\"\".format(public_url, port))\n","app.run()"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"ylIDIK3pbu32"},"outputs":[],"source":[]}],"metadata":{"colab":{"authorship_tag":"ABX9TyPm6m0DCBAwY8R/Ht2CEgVx","collapsed_sections":[],"mount_file_id":"1_Qz4yVKsgaS6lF2vvbz1_nIzNdg4zA4v","name":"011_LineChatbot基本組成_下載多媒體訊息.ipynb","provenance":[]},"kernelspec":{"display_name":"Python 3","name":"python3"}},"nbformat":4,"nbformat_minor":0} 2 | -------------------------------------------------------------------------------- /012_自主練習.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": { 7 | "id": "LI0SoqfUPWz8" 8 | }, 9 | "outputs": [], 10 | "source": [] 11 | } 12 | ], 13 | "metadata": { 14 | "colab": { 15 | "authorship_tag": "ABX9TyMCo8TgVDXQP/rg9vvjTEj2", 16 | "name": "010_自主練習.ipynb", 17 | "provenance": [] 18 | }, 19 | "kernelspec": { 20 | "display_name": "Python 3", 21 | "name": "python3" 22 | } 23 | }, 24 | "nbformat": 4, 25 | "nbformat_minor": 0 26 | } 27 | -------------------------------------------------------------------------------- /013_把line_bot_designer的消息變成json.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": { 7 | "id": "p0W867ZhdkdI" 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "'''\n", 12 | "\n", 13 | "資料 mapping 至google drive\n", 14 | "\n", 15 | "把資料寫在/content/drive\n", 16 | "\n", 17 | "即可保存在 google drive內\n", 18 | "\n", 19 | "'''\n", 20 | "from google.colab import drive\n", 21 | "drive.mount('/content/drive')" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": null, 27 | "metadata": { 28 | "id": "uB-ssmBtd5ac" 29 | }, 30 | "outputs": [], 31 | "source": [ 32 | "'''\n", 33 | "\n", 34 | "'''\n", 35 | "\n", 36 | "!pip install line-bot-sdk flask \n", 37 | "!pip install pyngrok" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": null, 43 | "metadata": { 44 | "id": "JS4BNBZQd7HU" 45 | }, 46 | "outputs": [], 47 | "source": [ 48 | "'''\n", 49 | "引用套件\n", 50 | "'''\n", 51 | "\n", 52 | "# 引用Web Server套件\n", 53 | "from flask import Flask, request, abort, jsonify\n", 54 | "\n", 55 | "# 載入json處理套件\n", 56 | "import json\n", 57 | "\n", 58 | "# 外部連結自動生成套件\n", 59 | "# from flask_ngrok import run_with_ngrok\n", 60 | "from pyngrok import ngrok\n", 61 | "\n", 62 | "# 從linebot 套件包裡引用 LineBotApi 與 WebhookHandler 類別\n", 63 | "from linebot import (\n", 64 | " LineBotApi, WebhookHandler\n", 65 | ")\n", 66 | "\n", 67 | "# 引用無效簽章錯誤\n", 68 | "from linebot.exceptions import (\n", 69 | " InvalidSignatureError\n", 70 | ")\n" 71 | ] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": null, 76 | "metadata": { 77 | "id": "DQjGb3Hjiy5U" 78 | }, 79 | "outputs": [], 80 | "source": [ 81 | "# 設定Server啟用細節\n", 82 | "app = Flask(__name__,static_url_path = \"/素材\" , static_folder = \"./素材/\")\n", 83 | "\n", 84 | "\n", 85 | "# 生成實體物件\n", 86 | "line_bot_api = LineBotApi(\"channel_access_token\")\n", 87 | "handler = WebhookHandler(\"secret_key\")" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": null, 93 | "metadata": { 94 | "id": "gUuI4lVzi5M7" 95 | }, 96 | "outputs": [], 97 | "source": [ 98 | "\n", 99 | "# 啟動server對外接口,使Line能丟消息進來\n", 100 | "@app.route(\"/\", methods=['POST'])\n", 101 | "def callback():\n", 102 | " # get X-Line-Signature header value\n", 103 | " signature = request.headers['X-Line-Signature']\n", 104 | "\n", 105 | " # get request body as text\n", 106 | " body = request.get_data(as_text=True)\n", 107 | " app.logger.info(\"Request body: \" + body)\n", 108 | "\n", 109 | " # handle webhook body\n", 110 | " try:\n", 111 | " handler.handle(body, signature)\n", 112 | " except InvalidSignatureError:\n", 113 | " abort(400)\n", 114 | "\n", 115 | " return 'OK'" 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "execution_count": null, 121 | "metadata": { 122 | "id": "iKUrvJ74jQMz" 123 | }, 124 | "outputs": [], 125 | "source": [ 126 | "'''\n", 127 | "消息判斷器\n", 128 | "讀取指定的json檔案後,把json解析成不同格式的SendMessage\n", 129 | "讀取檔案,\n", 130 | "把內容轉換成json\n", 131 | "將json轉換成消息\n", 132 | "放回array中,並把array傳出。\n", 133 | "'''\n", 134 | "\n", 135 | "# 引用會用到的套件\n", 136 | "from linebot.models import (\n", 137 | " ImagemapSendMessage,TextSendMessage,ImageSendMessage,LocationSendMessage,FlexSendMessage,VideoSendMessage\n", 138 | ")\n", 139 | "\n", 140 | "from linebot.models.template import (\n", 141 | " ButtonsTemplate,CarouselTemplate,ConfirmTemplate,ImageCarouselTemplate\n", 142 | " \n", 143 | ")\n", 144 | "\n", 145 | "from linebot.models.template import *\n", 146 | "\n", 147 | "def detect_json_array_to_new_message_array(fileName):\n", 148 | " \n", 149 | " #開啟檔案,轉成json\n", 150 | " with open(fileName,encoding='utf8') as f:\n", 151 | " jsonArray = json.load(f)\n", 152 | " \n", 153 | " # 解析json\n", 154 | " returnArray = []\n", 155 | " for jsonObject in jsonArray:\n", 156 | "\n", 157 | " # 讀取其用來判斷的元件\n", 158 | " message_type = jsonObject.get('type')\n", 159 | " \n", 160 | " # 轉換\n", 161 | " if message_type == 'text':\n", 162 | " returnArray.append(TextSendMessage.new_from_json_dict(jsonObject))\n", 163 | " elif message_type == 'imagemap':\n", 164 | " returnArray.append(ImagemapSendMessage.new_from_json_dict(jsonObject))\n", 165 | " elif message_type == 'template':\n", 166 | " returnArray.append(TemplateSendMessage.new_from_json_dict(jsonObject))\n", 167 | " elif message_type == 'image':\n", 168 | " returnArray.append(ImageSendMessage.new_from_json_dict(jsonObject))\n", 169 | " elif message_type == 'sticker':\n", 170 | " returnArray.append(StickerSendMessage.new_from_json_dict(jsonObject)) \n", 171 | " elif message_type == 'audio':\n", 172 | " returnArray.append(AudioSendMessage.new_from_json_dict(jsonObject)) \n", 173 | " elif message_type == 'location':\n", 174 | " returnArray.append(LocationSendMessage.new_from_json_dict(jsonObject))\n", 175 | " elif message_type == 'flex':\n", 176 | " returnArray.append(FlexSendMessage.new_from_json_dict(jsonObject)) \n", 177 | " elif message_type == 'video':\n", 178 | " returnArray.append(VideoSendMessage.new_from_json_dict(jsonObject)) \n", 179 | "\n", 180 | "\n", 181 | " # 回傳\n", 182 | " return returnArray\n" 183 | ] 184 | }, 185 | { 186 | "cell_type": "code", 187 | "execution_count": null, 188 | "metadata": { 189 | "id": "zWhCWzrfjge5" 190 | }, 191 | "outputs": [], 192 | "source": [ 193 | "\n", 194 | "# 引用套件\n", 195 | "from linebot.models import (\n", 196 | " MessageEvent, TextMessage\n", 197 | ")\n", 198 | "\n", 199 | "\n", 200 | "# 文字消息處理\n", 201 | "@handler.add(MessageEvent,message=TextMessage)\n", 202 | "def process_text_message(event):\n", 203 | "\n", 204 | " # 讀取本地檔案,並轉譯成消息\n", 205 | " result_message_array =[]\n", 206 | " replyJsonPath = event.message.text+\".json\"\n", 207 | " result_message_array = detect_json_array_to_new_message_array(replyJsonPath)\n", 208 | "\n", 209 | " # 發送\n", 210 | " line_bot_api.reply_message(\n", 211 | " event.reply_token,\n", 212 | " result_message_array\n", 213 | " )" 214 | ] 215 | }, 216 | { 217 | "cell_type": "code", 218 | "execution_count": null, 219 | "metadata": { 220 | "id": "fFwaojqca8F6" 221 | }, 222 | "outputs": [], 223 | "source": [ 224 | "# 主程序運行\n", 225 | "port = 5000\n", 226 | "# Open a ngrok tunnel to the HTTP server\n", 227 | "public_url = ngrok.connect(port).public_url\n", 228 | "print(\" * ngrok tunnel \\\"{}\\\" -> \\\"http://127.0.0.1:{}\\\"\".format(public_url, port))\n", 229 | "app.run()" 230 | ] 231 | } 232 | ], 233 | "metadata": { 234 | "colab": { 235 | "collapsed_sections": [], 236 | "name": "015_把line_bot_designer的消息變成json.ipynb", 237 | "provenance": [] 238 | }, 239 | "kernelspec": { 240 | "display_name": "Python 3", 241 | "language": "python", 242 | "name": "python3" 243 | }, 244 | "language_info": { 245 | "codemirror_mode": { 246 | "name": "ipython", 247 | "version": 3 248 | }, 249 | "file_extension": ".py", 250 | "mimetype": "text/x-python", 251 | "name": "python", 252 | "nbconvert_exporter": "python", 253 | "pygments_lexer": "ipython3", 254 | "version": "3.8.6" 255 | } 256 | }, 257 | "nbformat": 4, 258 | "nbformat_minor": 1 259 | } 260 | -------------------------------------------------------------------------------- /013_自主練習.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": { 7 | "id": "LI0SoqfUPWz8" 8 | }, 9 | "outputs": [], 10 | "source": [] 11 | } 12 | ], 13 | "metadata": { 14 | "colab": { 15 | "authorship_tag": "ABX9TyMCo8TgVDXQP/rg9vvjTEj2", 16 | "name": "010_自主練習.ipynb", 17 | "provenance": [] 18 | }, 19 | "kernelspec": { 20 | "display_name": "Python 3", 21 | "name": "python3" 22 | } 23 | }, 24 | "nbformat": 4, 25 | "nbformat_minor": 0 26 | } 27 | -------------------------------------------------------------------------------- /014_自主練習.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": { 7 | "id": "LI0SoqfUPWz8" 8 | }, 9 | "outputs": [], 10 | "source": [] 11 | } 12 | ], 13 | "metadata": { 14 | "colab": { 15 | "authorship_tag": "ABX9TyMCo8TgVDXQP/rg9vvjTEj2", 16 | "name": "010_自主練習.ipynb", 17 | "provenance": [] 18 | }, 19 | "kernelspec": { 20 | "display_name": "Python 3", 21 | "name": "python3" 22 | } 23 | }, 24 | "nbformat": 4, 25 | "nbformat_minor": 0 26 | } 27 | -------------------------------------------------------------------------------- /014_訊息推播.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": { 7 | "id": "QUmyHyZ3cnZI" 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "'''\n", 12 | "\n", 13 | "資料 mapping 至google drive\n", 14 | "\n", 15 | "把資料寫在/content/drive\n", 16 | "\n", 17 | "即可保存在 google drive內\n", 18 | "\n", 19 | "'''\n", 20 | "from google.colab import drive\n", 21 | "drive.mount('/content/drive')" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": null, 27 | "metadata": { 28 | "id": "IA4MtQz7c4vK" 29 | }, 30 | "outputs": [], 31 | "source": [ 32 | "'''\n", 33 | "\n", 34 | "'''\n", 35 | "\n", 36 | "!pip install line-bot-sdk flask " 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": null, 42 | "metadata": { 43 | "id": "vznr93WgdCkd" 44 | }, 45 | "outputs": [], 46 | "source": [ 47 | "from linebot import (\n", 48 | " LineBotApi, WebhookHandler\n", 49 | ")\n", 50 | "from linebot.models import (\n", 51 | " \n", 52 | " TextSendMessage\n", 53 | ")\n" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": null, 59 | "metadata": { 60 | "id": "gBxudgmsdMLj" 61 | }, 62 | "outputs": [], 63 | "source": [ 64 | "\n", 65 | "# 生成實體物件\n", 66 | "line_bot_api = LineBotApi(\"CHANNEL_ACCESS_TOKEN\")" 67 | ] 68 | }, 69 | { 70 | "cell_type": "code", 71 | "execution_count": null, 72 | "metadata": { 73 | "id": "N4IwX0MydOPN" 74 | }, 75 | "outputs": [], 76 | "source": [ 77 | "'''\n", 78 | "指定推播\n", 79 | "'''\n", 80 | "\n", 81 | "line_bot_api.push_message('USER_ID', TextSendMessage(text='Hello World!'))" 82 | ] 83 | }, 84 | { 85 | "cell_type": "code", 86 | "execution_count": null, 87 | "metadata": { 88 | "id": "5JSs8yHpcc1a" 89 | }, 90 | "outputs": [], 91 | "source": [ 92 | "'''\n", 93 | "廣播\n", 94 | "'''\n", 95 | "\n", 96 | "import json\n", 97 | "json_object_strings = open(\"/content/drive/MyDrive/users.txt\",'r')\n", 98 | "json_array = []\n", 99 | "user_id_array = []\n", 100 | "\n", 101 | "for json_object_string in json_object_strings:\n", 102 | " json_object = json.loads(json_object_string)\n", 103 | " json_array.append(json_object)\n", 104 | "\n", 105 | "for user_record in json_array:\n", 106 | " user_id_array.append(user_record.get(\"user_id\"))\n", 107 | "\n", 108 | "line_bot_api.multicast(\n", 109 | " user_id_array,\n", 110 | " TextSendMessage(text='Hello World!')\n", 111 | ")" 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": null, 117 | "metadata": { 118 | "id": "0YUQOJyRZepG" 119 | }, 120 | "outputs": [], 121 | "source": [] 122 | } 123 | ], 124 | "metadata": { 125 | "colab": { 126 | "collapsed_sections": [], 127 | "name": "013_訊息推播.ipynb", 128 | "provenance": [] 129 | }, 130 | "kernelspec": { 131 | "display_name": "Python 3", 132 | "language": "python", 133 | "name": "python3" 134 | }, 135 | "language_info": { 136 | "codemirror_mode": { 137 | "name": "ipython", 138 | "version": 3 139 | }, 140 | "file_extension": ".py", 141 | "mimetype": "text/x-python", 142 | "name": "python", 143 | "nbconvert_exporter": "python", 144 | "pygments_lexer": "ipython3", 145 | "version": "3.8.6" 146 | } 147 | }, 148 | "nbformat": 4, 149 | "nbformat_minor": 1 150 | } 151 | -------------------------------------------------------------------------------- /015_圖文選單建置.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "'''\n", 10 | "\n", 11 | "用戶菜單功能介紹\n", 12 | "\n", 13 | " 用戶能透過點擊菜單,進行我方希冀之業務功能。\n", 14 | " \n", 15 | "流程\n", 16 | " 準備菜單的圖面設定檔\n", 17 | " 讀取安全設定檔上的參數\n", 18 | " 將菜單設定檔傳給Line\n", 19 | " 對Line上傳菜單照片\n", 20 | " 檢視現有的菜單\n", 21 | " 將菜單與用戶做綁定\n", 22 | " 將菜單與用戶解除綁定\n", 23 | " 刪除菜單\n", 24 | "\n", 25 | "'''" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": null, 31 | "metadata": {}, 32 | "outputs": [], 33 | "source": [ 34 | "'''\n", 35 | "菜單設定檔\n", 36 | "\n", 37 | " 設定圖面大小、按鍵名與功能\n", 38 | " \n", 39 | "'''\n", 40 | "\n", 41 | "menuRawData=\"\"\"\n", 42 | "{\n", 43 | " \"size\": {\n", 44 | " \"width\": 2500,\n", 45 | " \"height\": 1686\n", 46 | " },\n", 47 | " \"selected\": true,\n", 48 | " \"name\": \"區塊鏈自定義菜單\",\n", 49 | " \"chatBarText\": \"查看更多資訊\",\n", 50 | " \"areas\": [\n", 51 | " {\n", 52 | " \"bounds\": {\n", 53 | " \"x\": 5,\n", 54 | " \"y\": 0,\n", 55 | " \"width\": 824,\n", 56 | " \"height\": 850\n", 57 | " },\n", 58 | " \"action\": {\n", 59 | " \"type\": \"message\",\n", 60 | " \"text\": \"[::text:]傳統交易\"\n", 61 | " }\n", 62 | " },\n", 63 | " {\n", 64 | " \"bounds\": {\n", 65 | " \"x\": 0,\n", 66 | " \"y\": 850,\n", 67 | " \"width\": 825,\n", 68 | " \"height\": 818\n", 69 | " },\n", 70 | " \"action\": {\n", 71 | " \"type\": \"message\",\n", 72 | " \"text\": \"[::text:]第三方公證人\"\n", 73 | " }\n", 74 | " },\n", 75 | " {\n", 76 | " \"bounds\": {\n", 77 | " \"x\": 829,\n", 78 | " \"y\": 5,\n", 79 | " \"width\": 871,\n", 80 | " \"height\": 849\n", 81 | " },\n", 82 | " \"action\": {\n", 83 | " \"type\": \"message\",\n", 84 | " \"text\": \"[::text:]多位公證人\"\n", 85 | " }\n", 86 | " },\n", 87 | " {\n", 88 | " \"bounds\": {\n", 89 | " \"x\": 825,\n", 90 | " \"y\": 854,\n", 91 | " \"width\": 875,\n", 92 | " \"height\": 814\n", 93 | " },\n", 94 | " \"action\": {\n", 95 | " \"type\": \"message\",\n", 96 | " \"text\": \"[::text:]多組織多位公證人\"\n", 97 | " }\n", 98 | " },\n", 99 | " {\n", 100 | " \"bounds\": {\n", 101 | " \"x\": 1700,\n", 102 | " \"y\": 0,\n", 103 | " \"width\": 800,\n", 104 | " \"height\": 858\n", 105 | " },\n", 106 | " \"action\": {\n", 107 | " \"type\": \"message\",\n", 108 | " \"text\": \"[::text:]教學訊息\"\n", 109 | " }\n", 110 | " },\n", 111 | " {\n", 112 | " \"bounds\": {\n", 113 | " \"x\": 1700,\n", 114 | " \"y\": 858,\n", 115 | " \"width\": 800,\n", 116 | " \"height\": 810\n", 117 | " },\n", 118 | " \"action\": {\n", 119 | " \"type\": \"message\",\n", 120 | " \"text\": \"[::text:]more\"\n", 121 | " }\n", 122 | " }\n", 123 | " ]\n", 124 | "}\n", 125 | "\"\"\"" 126 | ] 127 | }, 128 | { 129 | "cell_type": "code", 130 | "execution_count": null, 131 | "metadata": {}, 132 | "outputs": [], 133 | "source": [ 134 | "\n", 135 | "'''\n", 136 | "\n", 137 | "用channel_access_token創建line_bot_api,預備用來跟Line進行溝通\n", 138 | "\n", 139 | "'''\n", 140 | "\n", 141 | "from linebot import (\n", 142 | " LineBotApi, WebhookHandler\n", 143 | ")\n", 144 | "\n", 145 | "line_bot_api = LineBotApi(\"channel_access_token\")" 146 | ] 147 | }, 148 | { 149 | "cell_type": "code", 150 | "execution_count": null, 151 | "metadata": {}, 152 | "outputs": [], 153 | "source": [ 154 | "'''\n", 155 | "\n", 156 | "載入前面的圖文選單設定,\n", 157 | "\n", 158 | "並要求line_bot_api將圖文選單上傳至Line\n", 159 | " \n", 160 | "\n", 161 | "'''\n", 162 | "\n", 163 | "from linebot.models import RichMenu\n", 164 | "import requests\n", 165 | "\n", 166 | "menuJson=json.loads(menuRawData)\n", 167 | "\n", 168 | "lineRichMenuId = line_bot_api.create_rich_menu(rich_menu=RichMenu.new_from_json_dict(menuJson))\n", 169 | "print(lineRichMenuId)" 170 | ] 171 | }, 172 | { 173 | "cell_type": "code", 174 | "execution_count": null, 175 | "metadata": {}, 176 | "outputs": [], 177 | "source": [ 178 | "'''\n", 179 | "\n", 180 | "將先前準備的菜單照片,以Post消息寄發給Line\n", 181 | "\n", 182 | " 載入照片\n", 183 | " 要求line_bot_api,將圖片傳到先前的圖文選單id\n", 184 | "\n", 185 | "\n", 186 | "'''\n", 187 | "\n", 188 | "\n", 189 | "uploadImageFile=open(\"blockchain_demo.jpg\",'rb')\n", 190 | "\n", 191 | "setImageResponse = line_bot_api.set_rich_menu_image(lineRichMenuId,'image/jpeg',uploadImageFile)\n", 192 | "\n", 193 | "print(setImageResponse)" 194 | ] 195 | }, 196 | { 197 | "cell_type": "code", 198 | "execution_count": null, 199 | "metadata": {}, 200 | "outputs": [], 201 | "source": [ 202 | "'''\n", 203 | "\n", 204 | "將選單綁定到特定用戶身上\n", 205 | " 取出上面得到的菜單Id及用戶id\n", 206 | " 要求line_bot_api告知Line,將用戶與圖文選單做綁定\n", 207 | "\n", 208 | "'''\n", 209 | "\n", 210 | "# https://api.line.me/v2/bot/user/{userId}/richmenu/{richMenuId}\n", 211 | "\n", 212 | "\n", 213 | "linkResult=line_bot_api.link_rich_menu_to_user(\"SELF_USER_ID\", lineRichMenuId)\n", 214 | "\n", 215 | "print(linkResult)" 216 | ] 217 | }, 218 | { 219 | "cell_type": "code", 220 | "execution_count": null, 221 | "metadata": {}, 222 | "outputs": [], 223 | "source": [ 224 | "'''\n", 225 | "\n", 226 | "檢視用戶目前所綁定的菜單\n", 227 | " 取出用戶id,並告知line_bot_api,\n", 228 | " line_bot_api傳回用戶所綁定的菜單\n", 229 | " 印出\n", 230 | "\n", 231 | "'''\n", 232 | "\n", 233 | "# https://api.line.me/v2/bot/user/{userId}/richmenu\n", 234 | "\n", 235 | "\n", 236 | "rich_menu_id = line_bot_api.get_rich_menu_id_of_user(\"SELF_USER_ID\")\n", 237 | "print(rich_menu_id)" 238 | ] 239 | }, 240 | { 241 | "cell_type": "code", 242 | "execution_count": null, 243 | "metadata": {}, 244 | "outputs": [], 245 | "source": [ 246 | "'''\n", 247 | "\n", 248 | "解除選單與特定用戶的綁定\n", 249 | " 取出用戶id,並告知line_bot_api,\n", 250 | " line_bot_api解除用戶所綁定的菜單\n", 251 | "'''\n", 252 | "\n", 253 | "lineUnregisterUserMenuResponse=line_bot_api.unlink_rich_menu_from_user(\"SELF_USER_ID\")\n", 254 | "print(lineUnregisterUserMenuResponse)" 255 | ] 256 | }, 257 | { 258 | "cell_type": "code", 259 | "execution_count": null, 260 | "metadata": {}, 261 | "outputs": [], 262 | "source": [ 263 | "'''\n", 264 | "\n", 265 | "檢視帳號內,有哪些選單\n", 266 | " 要求line_bot_api,向line查詢我方的圖文選單列表\n", 267 | " 打印\n", 268 | "\n", 269 | "'''\n", 270 | "\n", 271 | "rich_menu_list = line_bot_api.get_rich_menu_list()\n", 272 | "for rich_menu in rich_menu_list:\n", 273 | " print(rich_menu.rich_menu_id)" 274 | ] 275 | } 276 | ], 277 | "metadata": { 278 | "kernelspec": { 279 | "display_name": "Python 3", 280 | "language": "python", 281 | "name": "python3" 282 | }, 283 | "language_info": { 284 | "codemirror_mode": { 285 | "name": "ipython", 286 | "version": 3 287 | }, 288 | "file_extension": ".py", 289 | "mimetype": "text/x-python", 290 | "name": "python", 291 | "nbconvert_exporter": "python", 292 | "pygments_lexer": "ipython3", 293 | "version": "3.8.6" 294 | } 295 | }, 296 | "nbformat": 4, 297 | "nbformat_minor": 4 298 | } 299 | -------------------------------------------------------------------------------- /016_teachable-machine_chatbot.ipynb: -------------------------------------------------------------------------------- 1 | {"cells":[{"cell_type":"code","execution_count":null,"metadata":{"id":"9OitNmq4FaJy"},"outputs":[],"source":["'''\n","\n","安裝套件\n","\n","'''\n","!pip install line-bot-sdk flask \n","!pip install pyngrok"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"t6qnOcS-FrsE"},"outputs":[],"source":["'''\n","解壓縮模型\n","'''\n","\n","from zipfile import ZipFile\n","\n","with ZipFile('converted_savedmodel.zip', 'r') as zipObj:\n"," # Extract all the contents of zip file in different directory\n"," zipObj.extractall('converted_savedmodel')"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"z1KdkjGaFtYc"},"outputs":[],"source":["'''\n","載入類別列表\n","'''\n","class_dict = {}\n","with open('converted_savedmodel/labels.txt') as f:\n"," for line in f:\n"," (key, val) = line.split()\n"," class_dict[int(key)] = val"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"Ce6T5eW2FvUz"},"outputs":[],"source":["from flask import Flask, request, abort\n","\n","from linebot import (\n"," LineBotApi, WebhookHandler\n",")\n","from linebot.exceptions import (\n"," InvalidSignatureError\n",")\n","from linebot.models import (\n"," MessageEvent, TextMessage, TextSendMessage,\n",")\n","\n","# 外部連結自動生成套件\n","# from flask_ngrok import run_with_ngrok\n","from pyngrok import ngrok\n","\n","# 設定Server啟用細節\n","app = Flask(__name__,static_url_path = \"/material\" , static_folder = \"./material/\")\n","\n","\n","line_bot_api = LineBotApi('CHANNEL_ACCESS_TOKEN')\n","handler = WebhookHandler('CHANNEL_SECRET')\n","\n","\n","@app.route(\"/\", methods=['POST'])\n","def callback():\n"," # get X-Line-Signature header value\n"," signature = request.headers['X-Line-Signature']\n","\n"," # get request body as text\n"," body = request.get_data(as_text=True)\n"," app.logger.info(\"Request body: \" + body)\n","\n"," # handle webhook body\n"," try:\n"," handler.handle(body, signature)\n"," except InvalidSignatureError:\n"," print(\"Invalid signature. Please check your channel access token/channel secret.\")\n"," abort(400)\n","\n"," return 'OK'"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"eNxeavGsF7Jy"},"outputs":[],"source":["'''\n","\n","關注事件,功能說明\n","\n","'''\n","\n","# 引用套件\n","from linebot.models import (\n"," FollowEvent,TextSendMessage\n",")\n","\n","# 關注事件處理\n","@handler.add(FollowEvent)\n","def process_follow_event(event):\n"," \n"," # 消息發送\n"," line_bot_api.reply_message(\n"," event.reply_token,\n"," TextSendMessage(\n"," \"\"\"這個教室裡面,我置入了兩個業配,當大家找到了業配,今天課程才算開始。請找到那個業配,拍下來並上傳。\"\"\"\n"," )\n"," )\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"5hAH0J8dGDy5"},"outputs":[],"source":["'''\n","\n","文字消息,功能說明\n","\n","'''\n","\n","# 引用套件\n","from linebot.models import (\n"," MessageEvent,TextMessage,TextSendMessage\n",")\n","\n","# 關注事件處理\n","@handler.add(MessageEvent,message=TextMessage)\n","def process_text_message_event(event):\n"," \n"," # 消息發送\n"," line_bot_api.reply_message(\n"," event.reply_token,\n"," TextSendMessage(\n"," \"\"\"這個教室裡面,我置入了兩個業配,當大家找到了業配,今天課程才算開始。請找到那個業配,拍下來並上傳。\"\"\"\n"," )\n"," )\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"1q5cZ5NaGHXM"},"outputs":[],"source":["'''\n","\n","圖片消息,解析圖片\n","\n","'''\n","\n","import tensorflow.keras\n","from PIL import Image, ImageOps\n","import numpy as np\n","\n","# 引用套件\n","from linebot.models import (\n"," MessageEvent,ImageMessage,TextSendMessage\n",")\n","\n","# Disable scientific notation for clarity\n","np.set_printoptions(suppress=True)\n","\n","# Load the model\n","model = tensorflow.keras.models.load_model('converted_savedmodel/model.savedmodel')\n","\n","import time\n","\n","@handler.add(MessageEvent, message=ImageMessage)\n","def handle_message(event):\n","\n"," print(time.asctime( time.localtime(time.time()) ))\n","\n"," message_content = line_bot_api.get_message_content(event.message.id)\n"," file_name = event.message.id+'.jpg'\n"," with open(file_name, 'wb') as fd:\n"," for chunk in message_content.iter_content():\n"," fd.write(chunk)\n","\n"," print(time.asctime( time.localtime(time.time()) ))\n","\n"," data = np.ndarray(shape=(1, 224, 224, 3), dtype=np.float32)\n","\n"," # Replace this with the path to your image\n"," image = Image.open(file_name)\n","\n"," #resize the image to a 224x224 with the same strategy as in TM2:\n"," #resizing the image to be at least 224x224 and then cropping from the center\n"," size = (224, 224)\n"," image = ImageOps.fit(image, size, Image.ANTIALIAS)\n","\n"," print(time.asctime( time.localtime(time.time()) ))\n"," \n"," #turn the image into a numpy array\n"," image_array = np.asarray(image)\n","\n"," # display the resized image\n"," image.show()\n","\n"," \n"," # Normalize the image\n"," normalized_image_array = (image_array.astype(np.float32) / 127.0 - 1 )\n","\n"," # Load the image into the array\n"," data = np.ndarray(shape=(1, 224, 224, 3), dtype=np.float32)\n"," data[0]= normalized_image_array[0:224,0:224,0:3]\n","\n"," # run the inference\n"," prediction = model.predict(data)\n","\n"," print(time.asctime( time.localtime(time.time()) ))\n","\n"," max_probability_item_index = np.argmax(prediction[0])\n","\n"," if prediction.max() > 0.6:\n"," line_bot_api.reply_message(\n"," event.reply_token,\n"," TextSendMessage(\n"," \"\"\"這個物件極有可能是 %s ,其相似機率為 %s \"\"\" %(class_dict.get(max_probability_item_index), prediction[0][max_probability_item_index])\n"," )\n"," )\n"," else :\n"," line_bot_api.reply_message(\n"," event.reply_token,\n"," TextSendMessage(\n"," \"\"\"再混啊!亂拍照!!\"\"\"\n"," )\n"," )"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"aix7yEkVGMqk"},"outputs":[],"source":["# 主程序運行\n","port = 5000\n","# Open a ngrok tunnel to the HTTP server\n","public_url = ngrok.connect(port).public_url\n","print(\" * ngrok tunnel \\\"{}\\\" -> \\\"http://127.0.0.1:{}\\\"\".format(public_url, port))\n","app.run()"]}],"metadata":{"colab":{"authorship_tag":"ABX9TyMi5WnSrVhB+57GY2fvq8Hp","collapsed_sections":[],"name":"016_teachable-machine_chatbot.ipynb","provenance":[]},"kernelspec":{"display_name":"Python 3","name":"python3"}},"nbformat":4,"nbformat_minor":0} 2 | -------------------------------------------------------------------------------- /017_荊無命的左手劍.ipynb: -------------------------------------------------------------------------------- 1 | {"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"017_荊無命的左手劍.ipynb","provenance":[],"collapsed_sections":[],"authorship_tag":"ABX9TyMCnxuVvZuCyE6MtEJtZ0/5"},"kernelspec":{"name":"python3","display_name":"Python 3"}},"cells":[{"cell_type":"markdown","metadata":{"id":"px76lfVKGsqk"},"source":["世人多聽聞,荊無命的左手劍很快\n","\n","其實荊無命的右手劍,更快。\n","\n","只是沒人提及,沒有活人提過。\n","\n","就像秉鴻\n","\n","世人多聽聞,他的AWS很熟\n","\n","其實他的GCP也很熟。\n","\n","但那都只是表象\n","\n","單身神功才是真\n","\n","橫練三年可出師\n","\n","閉關八年會發瘋"]},{"cell_type":"code","metadata":{"id":"g3rjDU9GGie-","executionInfo":{"status":"ok","timestamp":1606672319713,"user_tz":-480,"elapsed":708,"user":{"displayName":"李秉鴻","photoUrl":"","userId":"10672112514125515250"}}},"source":["'''\n","\n","'''\n","\n","# 若在本地端,得先 pip install google-cloud-storage\n","from google.cloud import storage\n","\n","storage_client = storage.Client.from_service_account_json('service_account.json')\n"],"execution_count":2,"outputs":[]},{"cell_type":"code","metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"wBc7jtVYHbXb","executionInfo":{"status":"ok","timestamp":1606672323807,"user_tz":-480,"elapsed":1007,"user":{"displayName":"李秉鴻","photoUrl":"","userId":"10672112514125515250"}},"outputId":"1629484f-c112-4198-b7d7-bfc6bd2eabda"},"source":["# 瀏覽有哪些桶子\n","# Make an authenticated API request\n","buckets = list(storage_client.list_buckets())\n","print(buckets)"],"execution_count":4,"outputs":[{"output_type":"stream","text":["[]\n"],"name":"stdout"}]},{"cell_type":"code","metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"uiGTmYoGIggC","executionInfo":{"status":"ok","timestamp":1606672779760,"user_tz":-480,"elapsed":882,"user":{"displayName":"李秉鴻","photoUrl":"","userId":"10672112514125515250"}},"outputId":"720afd2d-1013-438c-908f-d4dda1f4fc14"},"source":["# 上傳到指定的桶子內\n","# https://cloud.google.com/storage/docs/uploading-objects#storage-upload-object-code-sample\n","\n","bucket_name='your_gcp_bucket_name'\n","source_file_name= 'local_file'\n","destination_blob_name= 'storage-object-name'\n","\n","# 指定桶子\n","bucket = storage_client.bucket(bucket_name)\n","\n","# 指定本地檔案\n","blob = bucket.blob(destination_blob_name)\n","\n","# 指定遠端位置\n","blob.upload_from_filename(source_file_name)\n","\n","print(\n"," \"File {} uploaded to {}.\".format(\n"," source_file_name, destination_blob_name\n"," )\n",")"],"execution_count":5,"outputs":[{"output_type":"stream","text":["File sample_data/anscombe.json uploaded to destination.json.\n"],"name":"stdout"}]},{"cell_type":"code","metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"L1NCAI5EK6Ay","executionInfo":{"status":"ok","timestamp":1606672995870,"user_tz":-480,"elapsed":940,"user":{"displayName":"李秉鴻","photoUrl":"","userId":"10672112514125515250"}},"outputId":"9ee5ce79-0735-4ad5-9a39-84d14974dbe7"},"source":["# 瀏覽桶子內有多少檔案\n","# https://cloud.google.com/storage/docs/listing-objects#storage-list-objects-python\n","\n","bucket_name = \"your-bucket-name\"\n","\n","blobs = storage_client.list_blobs(bucket_name)\n","\n","for blob in blobs:\n"," print(blob.name)"],"execution_count":6,"outputs":[{"output_type":"stream","text":["destination.json\n","gcp-cloud-native-demo.tar\n","rich_menu.jpg\n"],"name":"stdout"}]},{"cell_type":"code","metadata":{"id":"X3_SyGlwKZfq"},"source":["# 下載到指定的地方\n","# https://cloud.google.com/storage/docs/downloading-objects#code-samples\n","\n","\"\"\"Downloads a blob from the bucket.\"\"\"\n","bucket_name = \"your-bucket-name\"\n","source_blob_name = \"storage-object-name\"\n","destination_file_name = \"local/path/to/file\"\n","\n","bucket = storage_client.bucket(bucket_name)\n","blob = bucket.blob(source_blob_name)\n","blob.download_to_filename(destination_file_name)\n","\n","print(\n"," \"Blob {} downloaded to {}.\".format(\n"," source_blob_name, destination_file_name\n"," )\n",")"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"Xf4gyXVPLHb2"},"source":["# 刪除桶子內的物件\n","# https://cloud.google.com/storage/docs/deleting-objects\n","\n","bucket_name = \"your-bucket-name\"\n","blob_name = \"your-object-name\"\n","\n","bucket = storage_client.bucket(bucket_name)\n","blob = bucket.blob(blob_name)\n","blob.delete()\n","\n","print(\"Blob {} deleted.\".format(blob_name))"],"execution_count":null,"outputs":[]}]} -------------------------------------------------------------------------------- /018_GCP與Chatbot.ipynb: -------------------------------------------------------------------------------- 1 | {"cells":[{"cell_type":"code","execution_count":null,"metadata":{"id":"uB7UXHtGL6CG"},"outputs":[],"source":["!pip install line-bot-sdk flask \n","!pip install pyngrok"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"5GjZFYH4MNP_"},"outputs":[],"source":["'''\n","引用套件\n","'''\n","\n","# 引用Web Server套件\n","from flask import Flask, request, abort, jsonify\n","\n","# 載入json處理套件\n","import json\n","\n","# 外部連結自動生成套件\n","# from flask_ngrok import run_with_ngrok\n","from pyngrok import ngrok\n","\n","# 從linebot 套件包裡引用 LineBotApi 與 WebhookHandler 類別\n","from linebot import (\n"," LineBotApi, WebhookHandler\n",")\n","\n","# 引用無效簽章錯誤\n","from linebot.exceptions import (\n"," InvalidSignatureError\n",")\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"4Y7U7MOEMQqj"},"outputs":[],"source":["'''\n","建置主程序\n","\n","建置handler與 line_bot_api\n","'''\n","\n","# 設定Server啟用細節\n","app = Flask(__name__,static_url_path = \"/material\" , static_folder = \"./material/\")\n","\n","\n","# 生成實體物件\n","line_bot_api = LineBotApi(\"CHANNEL_ACCESS_TOKEN\")\n","handler = WebhookHandler(\"CHANNEL_SECRET\")\n","\n","from google.cloud import storage\n","\n","# 本地端才如此作為,最好改用環境變數\n","storage_client = storage.Client.from_service_account_json('service_account.json')"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"MA2sIy_3MRoS"},"outputs":[],"source":["'''\n","建置主程序的API入口\n"," 接受Line傳過來的消息\n"," 並取出消息內容\n"," 將消息內容存在google drive的檔案內\n"," 並請handler 進行消息驗證與轉發\n","'''\n","\n","# 啟動server對外接口,使Line能丟消息進來\n","@app.route(\"/\", methods=['POST'])\n","def callback():\n"," # get X-Line-Signature header value\n"," signature = request.headers['X-Line-Signature']\n","\n"," # get request body as text\n"," body = request.get_data(as_text=True)\n"," print(body)\n","\n"," # 記錄用戶log,請去查更正確的logging作法\n"," f = open(\"./ai-event.log\", \"a\")\n"," f.write(body)\n"," f.close()\n","\n"," # handle webhook body\n"," try:\n"," handler.handle(body, signature)\n"," except InvalidSignatureError:\n"," abort(400)\n","\n"," return 'OK'"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"Wr14RAgDMmb4"},"outputs":[],"source":["'''\n","\n","若收到圖片消息時,\n","\n","先回覆用戶文字消息,並從Line上將照片拿回。\n","\n","'''\n","\n","from linebot.models import(\n"," MessageEvent,ImageMessage, TextSendMessage\n",")\n","\n","@handler.add(MessageEvent, message=ImageMessage)\n","def handle_message(event):\n"," \n"," line_bot_api.reply_message(\n"," event.reply_token,\n"," TextSendMessage(text='Image has Upload'+ ' ' + event.message.id))\n"," \n"," message_content = line_bot_api.get_message_content(event.message.id)\n"," with open(event.message.id+'.jpg', 'wb') as fd:\n"," for chunk in message_content.iter_content():\n"," fd.write(chunk)\n","\n"," bucket_name='your_gcp_bucket_name'\n"," source_file_name= event.message.id+'.jpg'\n"," destination_blob_name= event.source.user_id +'/'+ event.message.id+'.jpg'\n","\n"," bucket = storage_client.bucket(bucket_name)\n","\n"," blob = bucket.blob(destination_blob_name)\n","\n"," blob.upload_from_filename(source_file_name)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"8y0AEVmkMqDm"},"outputs":[],"source":["# 主程序運行\n","port = 5000\n","# Open a ngrok tunnel to the HTTP server\n","public_url = ngrok.connect(port).public_url\n","print(\" * ngrok tunnel \\\"{}\\\" -> \\\"http://127.0.0.1:{}\\\"\".format(public_url, port))\n","app.run()"]}],"metadata":{"colab":{"authorship_tag":"ABX9TyPY7yMZKzfyXWYuUNvjANAy","name":"018_GCP與Chatbot.ipynb","provenance":[]},"kernelspec":{"display_name":"Python 3","name":"python3"}},"nbformat":4,"nbformat_minor":0} 2 | -------------------------------------------------------------------------------- /019_GCP與Chatbot_2.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "name": "019_GCP與Chatbot-2.ipynb", 7 | "provenance": [], 8 | "collapsed_sections": [] 9 | }, 10 | "kernelspec": { 11 | "name": "python3", 12 | "display_name": "Python 3" 13 | } 14 | }, 15 | "cells": [ 16 | { 17 | "cell_type": "code", 18 | "metadata": { 19 | "id": "uB7UXHtGL6CG" 20 | }, 21 | "source": [ 22 | "!pip install line-bot-sdk flask flask-ngrok \n", 23 | "\n", 24 | "# 安裝後必須點擊Runtime -> Restart Runtime\n", 25 | "!pip install google-cloud-texttospeech\n" 26 | ], 27 | "execution_count": null, 28 | "outputs": [] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "metadata": { 33 | "id": "5GjZFYH4MNP_" 34 | }, 35 | "source": [ 36 | "'''\n", 37 | "引用套件\n", 38 | "'''\n", 39 | "\n", 40 | "# 引用Web Server套件\n", 41 | "from flask import Flask, request, abort, jsonify\n", 42 | "\n", 43 | "# 載入json處理套件\n", 44 | "import json\n", 45 | "\n", 46 | "# 外部連結自動生成套件\n", 47 | "from flask_ngrok import run_with_ngrok\n", 48 | "\n", 49 | "# 從linebot 套件包裡引用 LineBotApi 與 WebhookHandler 類別\n", 50 | "from linebot import (\n", 51 | " LineBotApi, WebhookHandler\n", 52 | ")\n", 53 | "\n", 54 | "# 引用無效簽章錯誤\n", 55 | "from linebot.exceptions import (\n", 56 | " InvalidSignatureError\n", 57 | ")\n" 58 | ], 59 | "execution_count": null, 60 | "outputs": [] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "metadata": { 65 | "id": "4Y7U7MOEMQqj" 66 | }, 67 | "source": [ 68 | "'''\n", 69 | "建置主程序\n", 70 | "\n", 71 | "建置handler與 line_bot_api\n", 72 | "'''\n", 73 | "\n", 74 | "# 設定Server啟用細節\n", 75 | "app = Flask(__name__,static_url_path = \"/material\" , static_folder = \"./material/\")\n", 76 | "run_with_ngrok(app)\n", 77 | "\n", 78 | "# 生成實體物件\n", 79 | "line_bot_api = LineBotApi(\"CHANNEL_ACCESS_TOKEN\")\n", 80 | "handler = WebhookHandler(\"CHANNEL_SECRET\")\n", 81 | " \n", 82 | "from google.cloud import storage\n", 83 | "from google.cloud import texttospeech\n", 84 | "# 本地端才如此作為,最好改用環境變數\n", 85 | "storage_client = storage.Client.from_service_account_json('service_account.json')\n", 86 | "bucket_name='GCP Cloud Storage Bucket name'\n", 87 | "\n", 88 | "# Instantiates a client\n", 89 | "speech_client = texttospeech.TextToSpeechClient.from_service_account_json('service_account.json')" 90 | ], 91 | "execution_count": null, 92 | "outputs": [] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "metadata": { 97 | "id": "MA2sIy_3MRoS" 98 | }, 99 | "source": [ 100 | "'''\n", 101 | "建置主程序的API入口\n", 102 | " 接受Line傳過來的消息\n", 103 | " 並取出消息內容\n", 104 | " 將消息內容存在google drive的檔案內\n", 105 | " 並請handler 進行消息驗證與轉發\n", 106 | "'''\n", 107 | "\n", 108 | "# 啟動server對外接口,使Line能丟消息進來\n", 109 | "@app.route(\"/\", methods=['POST'])\n", 110 | "def callback():\n", 111 | " # get X-Line-Signature header value\n", 112 | " signature = request.headers['X-Line-Signature']\n", 113 | "\n", 114 | " # get request body as text\n", 115 | " body = request.get_data(as_text=True)\n", 116 | " print(body)\n", 117 | "\n", 118 | " # 記錄用戶log,請去查更正確的logging作法\n", 119 | " f = open(\"./ai-event.log\", \"a\")\n", 120 | " f.write(body)\n", 121 | " f.close()\n", 122 | "\n", 123 | " # handle webhook body\n", 124 | " try:\n", 125 | " handler.handle(body, signature)\n", 126 | " except InvalidSignatureError:\n", 127 | " abort(400)\n", 128 | "\n", 129 | " return 'OK'" 130 | ], 131 | "execution_count": null, 132 | "outputs": [] 133 | }, 134 | { 135 | "cell_type": "code", 136 | "metadata": { 137 | "id": "Wr14RAgDMmb4" 138 | }, 139 | "source": [ 140 | "'''\n", 141 | "\n", 142 | "若收到圖片消息時,\n", 143 | "\n", 144 | "先回覆用戶文字消息,並從Line上將照片拿回。\n", 145 | "\n", 146 | "'''\n", 147 | "\n", 148 | "from linebot.models import(\n", 149 | " MessageEvent,ImageMessage, TextSendMessage\n", 150 | ")\n", 151 | "\n", 152 | "@handler.add(MessageEvent, message=ImageMessage)\n", 153 | "def handle_message(event):\n", 154 | " \n", 155 | " line_bot_api.reply_message(\n", 156 | " event.reply_token,\n", 157 | " TextSendMessage(text='Image has Upload'+ ' ' + event.message.id))\n", 158 | " \n", 159 | " message_content = line_bot_api.get_message_content(event.message.id)\n", 160 | " with open(event.message.id+'.jpg', 'wb') as fd:\n", 161 | " for chunk in message_content.iter_content():\n", 162 | " fd.write(chunk)\n", 163 | "\n", 164 | "\n", 165 | " source_file_name= event.message.id+'.jpg'\n", 166 | " destination_blob_name= event.source.user_id +'/'+ event.message.id+'.jpg'\n", 167 | "\n", 168 | " bucket = storage_client.bucket(bucket_name)\n", 169 | "\n", 170 | " blob = bucket.blob(destination_blob_name)\n", 171 | "\n", 172 | " blob.upload_from_filename(source_file_name)" 173 | ], 174 | "execution_count": null, 175 | "outputs": [] 176 | }, 177 | { 178 | "cell_type": "code", 179 | "metadata": { 180 | "id": "2avfC7bLxriD" 181 | }, 182 | "source": [ 183 | "'''\n", 184 | "若收到文字消息時\n", 185 | "\n", 186 | "將文字傳給GCP,轉譯成音訊後\n", 187 | "\n", 188 | "放到cloud storage,並組合成AudioSendMessage,傳回給用戶\n", 189 | "\n", 190 | "Podcast\n", 191 | "'''\n", 192 | "\n", 193 | "from linebot.models import(\n", 194 | " MessageEvent,TextMessage, AudioSendMessage\n", 195 | ")\n", 196 | "\n", 197 | "\n", 198 | "@handler.add(MessageEvent, message=TextMessage)\n", 199 | "def handle_text_message(event):\n", 200 | "\n", 201 | " # Set the text input to be synthesized\n", 202 | " synthesis_input = texttospeech.SynthesisInput(text=event.message.text)\n", 203 | "\n", 204 | "\n", 205 | " # Build the voice request, select the language code (\"en-US\") and the ssml\n", 206 | " # voice gender (\"neutral\")\n", 207 | " voice = texttospeech.VoiceSelectionParams(\n", 208 | " language_code=\"zh-TW\", ssml_gender=texttospeech.SsmlVoiceGender.NEUTRAL\n", 209 | " )\n", 210 | "\n", 211 | " # Select the type of audio file you want returned\n", 212 | " audio_config = texttospeech.AudioConfig(\n", 213 | " audio_encoding=texttospeech.AudioEncoding.MP3\n", 214 | " )\n", 215 | "\n", 216 | " # Perform the text-to-speech request on the text input with the selected\n", 217 | " # voice parameters and audio file type\n", 218 | " response = speech_client.synthesize_speech(\n", 219 | " input=synthesis_input, voice=voice, audio_config=audio_config\n", 220 | " )\n", 221 | "\n", 222 | " # The response's audio_content is binary.\n", 223 | " voice_mp3_file_path=event.message.id+\".mp3\"\n", 224 | " with open(voice_mp3_file_path, \"wb\") as out:\n", 225 | " # Write the response to the output file.\n", 226 | " out.write(response.audio_content)\n", 227 | " print('Audio content written to file '+voice_mp3_file_path)\n", 228 | "\n", 229 | "\n", 230 | " # 上傳檔案\n", 231 | " destination_blob_name= event.source.user_id +'/'+ event.message.id+'.mp3'\n", 232 | "\n", 233 | " bucket = storage_client.bucket(bucket_name)\n", 234 | "\n", 235 | " blob = bucket.blob(destination_blob_name)\n", 236 | "\n", 237 | " blob.upload_from_filename(voice_mp3_file_path)\n", 238 | "\n", 239 | " # 回話\n", 240 | " line_bot_api.reply_message(\n", 241 | " event.reply_token,\n", 242 | " AudioSendMessage(\n", 243 | " original_content_url =\"https://storage.googleapis.com/\"+bucket_name+'/'+destination_blob_name,\n", 244 | " duration=60000\n", 245 | " ) \n", 246 | " )" 247 | ], 248 | "execution_count": null, 249 | "outputs": [] 250 | }, 251 | { 252 | "cell_type": "code", 253 | "metadata": { 254 | "id": "5oYoKWEXq24z" 255 | }, 256 | "source": [ 257 | "'''\n", 258 | "Google ML 參考\n", 259 | "https://colab.research.google.com/github/robeartoe/APIWorkshop/blob/master/MLWorkshop.ipynb#scrollTo=Snq8ehAu7JkD\n", 260 | "\n", 261 | "'''" 262 | ], 263 | "execution_count": null, 264 | "outputs": [] 265 | }, 266 | { 267 | "cell_type": "code", 268 | "metadata": { 269 | "id": "8y0AEVmkMqDm" 270 | }, 271 | "source": [ 272 | "# 主程序運行\n", 273 | "app.run()" 274 | ], 275 | "execution_count": null, 276 | "outputs": [] 277 | }, 278 | { 279 | "cell_type": "code", 280 | "metadata": { 281 | "id": "DEVizlul75HY" 282 | }, 283 | "source": [ 284 | "" 285 | ], 286 | "execution_count": null, 287 | "outputs": [] 288 | } 289 | ] 290 | } -------------------------------------------------------------------------------- /020_Chatbot資料清洗.ipynb: -------------------------------------------------------------------------------- 1 | {"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"020_Chatbot資料清洗.ipynb","provenance":[],"collapsed_sections":[]},"kernelspec":{"name":"python3","display_name":"Python 3"},"language_info":{"name":"python"}},"cells":[{"cell_type":"code","metadata":{"id":"g3j7MNeA37cO"},"source":["'''\n","開始前,必須先去下載 GCP 為我們搜集的Log\n","並把名字改為 downloaded-logs.csv\n","上傳到colab\n","\n","'''"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"NGt_Zdl0Q54h"},"source":["'''\n","讀取csv\n","\n","python read csv\n","https://www.shanelynn.ie/python-pandas-read-csv-load-data-from-csv-files/\n","'''\n","import pandas as pd \n","data = pd.read_csv(\"downloaded-logs.csv\") \n","data"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"-qR-HSi3RBTJ"},"source":["'''\n","抓出指定欄位 - jsonPayload.message\n","\n","'''\n","data['jsonPayload.message']"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"vsKMNOUERI_w"},"source":["'''\n","將jsonPayload.message 的欄位內容 轉成csv\n","\n","欄位若無值,則不填\n","\n","'''\n","import json\n","json_data_list = []\n","\n","\n","df_message = pd.DataFrame(columns=['userId', 'timestamp', 'event_type','message_type','message_id', 'message_text'])\n","\n","for row in data['jsonPayload.message']:\n"," if json.loads(row).get('events'):\n"," events = json.loads(row).get('events')[0]\n"," print(json.loads(row).get('events')[0])\n"," # json_data_list.append()\n"," # print(line_event.get('type'))\n"," # print(line_event.get('message'))\n"," # print(line_event.get('message').get('id'))\n"," # print(line_event.get('timestamp'))\n"," # print(line_event.get('source').get('userId'))\n"," if events.get('type') not in ['follow','unfollow','postback']:\n"," df_message = df_message.append(\n"," {\n"," 'userId': events.get('source').get('userId'), \n"," 'timestamp': events.get('timestamp'), \n"," 'event_type': events.get('type'),\n"," 'message_type': events.get('message').get('type'),\n"," 'message_id': events.get('message').get('id'),\n"," 'message_text': events.get('message').get('text')\n"," },\n"," ignore_index=True)\n"," elif events.get('type') in ['postback']:\n"," df_message =df_message.append(\n"," {\n"," 'userId': events.get('source').get('userId'), \n"," 'timestamp': events.get('timestamp'), \n"," 'event_type': events.get('type'),\n"," 'message_text': events.get('postback').get('data')\n"," },\n"," ignore_index=True\n"," )\n"," else:\n"," df_message = df_message.append(\n"," {\n"," 'userId': events.get('source').get('userId'), \n"," 'timestamp': events.get('timestamp'), \n"," 'event_type': events.get('type')\n"," },\n"," ignore_index=True)\n","\n","df_message"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"GQ2Acmp_q305"},"source":["# 安裝line 套件\n","!pip install line-bot-sdk"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"rZapuzNHe-T6"},"source":["'''\n","\n","設置Line Bot Api 的 Channel access token\n","\n","'''\n","\n","from linebot import (\n"," LineBotApi, WebhookHandler\n",")\n","\n","line_bot_api = LineBotApi('CHANNEL_ACCESS_TOKEN')"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"QHFKJJEfTn2t"},"source":["'''\n","取出用戶id,向Line要個資\n","整理成一份用戶個資名單\n","'''\n","\n","\n","user_info_df= pd.DataFrame(columns=['userId', 'display_name', 'picture_url','status_message'])\n","\n","for user_id in df_message['userId'].unique():\n"," try:\n"," line_user_profile = line_bot_api.get_profile(user_id)\n"," \n"," user_info_df = user_info_df.append(\n"," {\n"," 'display_name': line_user_profile.display_name, \n"," 'userId': line_user_profile.user_id, \n"," 'picture_url': line_user_profile.picture_url,\n"," 'status_message': line_user_profile.status_message\n"," },\n"," ignore_index=True)\n"," except:\n"," pass\n","\n","user_info_df"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"GJ63oQA2Ttbr"},"source":["'''\n","\n","將用戶個資名單跟 jsonPayload的欄位內容 整理在一份csv\n","\n","'''\n","merge_data = pd.merge(df_message, user_info_df)\n","merge_data"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"iY66ofIAwrKq"},"source":["'''\n","timestamp 轉換成人類可懂的時間\n","'''\n","\n","import datetime\n","human_use_date=[]\n","for stamp in merge_data['timestamp']:\n"," \n"," timestamp = datetime.datetime.fromtimestamp(\n"," stamp/1000+8*3600\n"," )\n"," human_use_date.append(timestamp.strftime('%Y-%m-%d %H:%M:%S'))\n"," \n","\n","merge_data['human_date']=human_use_date\n","merge_data"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"7TBu2zXGu9zi"},"source":["'''\n","將資料存成csv\n","'''\n","merge_data.to_csv(\"final_data.csv\")"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"FDhh_B2_vozm"},"source":["'''\n","\n","進行可視化\n","\n","外國人不考慮中文,中文會是亂碼\n","\n","'''\n","\n"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"G7mce7UVv1vv"},"source":["'''\n","看看誰發的話多\n","'''\n","\n","merge_data['userId'].value_counts().sort_index().plot.bar()\n","# merge_data['timestamp'].plot.hist()"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"uBCmwm0hwAjL"},"source":["'''\n","看看都發哪一類型的消息\n","'''\n","merge_data['message_type'].value_counts().sort_index().plot.bar()"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"tIAElj003MhS"},"source":[""],"execution_count":null,"outputs":[]}]} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # 使用須知 3 | 4 | 參閱 5 | 6 | https://colab.research.google.com/github/googlecolab/colabtools/blob/master/notebooks/colab-github-demo.ipynb#scrollTo=8QAWNjizy_3O 7 | 8 | 可透過colab讀取github內容,在該ipynb的網址前面,貼上此連結 9 | 10 | https://colab.research.google.com/github/ 11 | -------------------------------------------------------------------------------- /blockchain_demo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BingHongLi/ai-chatbot-tutorial/918be2e6dd2c7e88909909bf009a1d068fd14ebc/blockchain_demo.jpg --------------------------------------------------------------------------------