├── D10 ├── runtime.txt ├── Procfile ├── requirements.txt ├── app_core.py └── app_echo.py ├── D11 ├── runtime.txt ├── Procfile ├── requirements.txt ├── config.ini ├── 1_app_core.py ├── 2_app_echo.py ├── 3_app_echo.py └── 4_app_pretty_echo.py ├── D12 ├── runtime.txt ├── Procfile ├── requirements.txt ├── config.ini ├── app_pixabay.py └── app_google_isch.py ├── D14 ├── runtime.txt ├── Procfile ├── requirements.txt ├── config.ini ├── templates │ └── home.html ├── clock.py └── app_pixabay.py ├── D17 ├── runtime.txt ├── Procfile ├── config.ini ├── requirements.txt ├── templates │ ├── home.html │ └── home_pixijs.html ├── custom_models │ ├── CallDatabase.py │ ├── PhoebeTalks.py │ └── utils.py ├── clock.py └── app_record.py ├── D19 ├── runtime.txt ├── Procfile ├── config.ini ├── requirements.txt ├── templates │ ├── home.html │ └── home_pixijs.html ├── custom_models │ ├── CallDatabase.py │ ├── PhoebeFlex.py │ ├── PhoebeTalks.py │ └── utils.py ├── clock.py └── app_day_19.py ├── D20 ├── runtime.txt ├── Procfile ├── config.ini ├── requirements.txt ├── static │ └── img │ │ └── alpaca_logo.ico ├── templates │ └── home.html ├── custom_models │ ├── CallDatabase.py │ ├── PhoebeFlex.py │ ├── PhoebeTalks.py │ └── utils.py ├── clock.py └── app_day_20.py ├── D22 ├── runtime.txt ├── Procfile ├── config.ini ├── requirements.txt ├── static │ ├── img │ │ └── alpaca_logo.ico │ └── css │ │ └── custom.css ├── custom_models │ ├── CallDatabase.py │ ├── PhoebeFlex.py │ ├── PhoebeTalks.py │ └── utils.py ├── templates │ ├── from_start.html │ ├── home.html │ └── base.html ├── clock.py └── app_day_22.py ├── D23 ├── runtime.txt ├── Procfile ├── config.ini ├── requirements.txt ├── static │ ├── img │ │ └── alpaca_logo.ico │ └── css │ │ └── custom.css ├── custom_models │ ├── CallDatabase.py │ ├── PhoebeFlex.py │ ├── PhoebeTalks.py │ └── utils.py ├── templates │ ├── from_start.html │ ├── show_records.html │ ├── home.html │ └── base.html ├── clock.py └── app_day_23.py ├── D24 ├── runtime.txt ├── Procfile ├── static │ ├── css │ │ └── custom.css │ └── img │ │ └── alpaca_logo.ico ├── config.ini ├── requirements.txt ├── templates │ ├── from_start.html │ ├── show_records.html │ ├── home.html │ ├── select_records.html │ ├── select_records_comfortable.html │ └── base.html ├── clock.py ├── custom_models │ ├── PhoebeFlex.py │ ├── PhoebeTalks.py │ ├── CallDatabase.py │ └── utils.py └── app_day_24.py ├── D25 ├── runtime.txt ├── Procfile ├── static │ ├── css │ │ └── custom.css │ └── img │ │ └── alpaca_logo.ico ├── requirements.txt ├── config.ini ├── templates │ ├── show_records.html │ ├── home.html │ ├── from_start.html │ ├── select_records.html │ ├── select_records_comfortable.html │ ├── login.html │ └── base.html ├── clock.py └── custom_models │ ├── PhoebeFlex.py │ ├── PhoebeTalks.py │ ├── CallDatabase.py │ └── utils.py ├── D27 ├── runtime.txt ├── Procfile ├── static │ ├── css │ │ └── custom.css │ └── img │ │ └── alpaca_logo.ico ├── requirements.txt ├── config.ini ├── templates │ ├── show_records.html │ ├── home.html │ ├── from_start.html │ ├── select_records.html │ ├── select_records_comfortable.html │ ├── login.html │ └── donut_chart.html ├── clock.py └── custom_models │ ├── PhoebeFlex.py │ ├── PhoebeTalks.py │ ├── CallDatabase.py │ └── utils.py ├── D29 ├── runtime.txt ├── Procfile ├── static │ ├── css │ │ └── custom.css │ └── img │ │ └── alpaca_logo.ico ├── requirements.txt ├── config.ini ├── templates │ ├── show_records.html │ ├── home.html │ ├── from_start.html │ ├── select_records.html │ ├── select_records_comfortable.html │ ├── login.html │ └── donut_chart.html ├── clock.py └── custom_models │ ├── PhoebeFlex.py │ ├── PhoebeTalks.py │ └── CallDatabase.py ├── README.md └── D15 - Heroku Postgres.ipynb /D10/runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.6.9 -------------------------------------------------------------------------------- /D11/runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.6.9 -------------------------------------------------------------------------------- /D12/runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.6.9 -------------------------------------------------------------------------------- /D14/runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.6.9 -------------------------------------------------------------------------------- /D17/runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.6.9 -------------------------------------------------------------------------------- /D19/runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.6.9 -------------------------------------------------------------------------------- /D20/runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.6.9 -------------------------------------------------------------------------------- /D22/runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.6.9 -------------------------------------------------------------------------------- /D23/runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.6.9 -------------------------------------------------------------------------------- /D24/runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.6.9 -------------------------------------------------------------------------------- /D25/runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.6.9 -------------------------------------------------------------------------------- /D27/runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.6.9 -------------------------------------------------------------------------------- /D29/runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.6.9 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Catcher-in-the-LINE -------------------------------------------------------------------------------- /D10/Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn app_echo:app --preload -------------------------------------------------------------------------------- /D12/Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn app_pixabay:app --preload -------------------------------------------------------------------------------- /D11/Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn 4_app_pretty_echo:app --preload -------------------------------------------------------------------------------- /D10/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.1.1 2 | gunicorn==19.9.0 3 | line-bot-sdk==1.14.0 -------------------------------------------------------------------------------- /D11/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.1.1 2 | gunicorn==19.9.0 3 | line-bot-sdk==1.14.0 -------------------------------------------------------------------------------- /D12/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.1.1 2 | gunicorn==19.9.0 3 | line-bot-sdk==1.14.0 -------------------------------------------------------------------------------- /D14/Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn app_pixabay:app --preload 2 | clock: python clock.py -------------------------------------------------------------------------------- /D17/Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn app_record:app --preload 2 | clock: python clock.py -------------------------------------------------------------------------------- /D19/Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn app_day_19:app --preload 2 | clock: python clock.py -------------------------------------------------------------------------------- /D20/Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn app_day_20:app --preload 2 | clock: python clock.py -------------------------------------------------------------------------------- /D22/Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn app_day_22:app --preload 2 | clock: python clock.py -------------------------------------------------------------------------------- /D23/Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn app_day_23:app --preload 2 | clock: python clock.py -------------------------------------------------------------------------------- /D24/Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn app_day_23:app --preload 2 | clock: python clock.py -------------------------------------------------------------------------------- /D25/Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn app_day_25:app --preload 2 | clock: python clock.py -------------------------------------------------------------------------------- /D27/Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn app_day_27:app --preload 2 | clock: python clock.py -------------------------------------------------------------------------------- /D29/Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn app_day_29:app --preload 2 | clock: python clock.py -------------------------------------------------------------------------------- /D14/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.1.1 2 | gunicorn==19.9.0 3 | line-bot-sdk==1.14.0 4 | APScheduler==3.6.1 -------------------------------------------------------------------------------- /D24/static/css/custom.css: -------------------------------------------------------------------------------- 1 | .bg-dirty-purple {background-color: Indigo;} 2 | 3 | .desc {height: 20em;} -------------------------------------------------------------------------------- /D25/static/css/custom.css: -------------------------------------------------------------------------------- 1 | .bg-dirty-purple {background-color: Indigo;} 2 | 3 | .desc {height: 20em;} -------------------------------------------------------------------------------- /D27/static/css/custom.css: -------------------------------------------------------------------------------- 1 | .bg-dirty-purple {background-color: Indigo;} 2 | 3 | .desc {height: 20em;} -------------------------------------------------------------------------------- /D29/static/css/custom.css: -------------------------------------------------------------------------------- 1 | .bg-dirty-purple {background-color: Indigo;} 2 | 3 | .desc {height: 20em;} -------------------------------------------------------------------------------- /D11/config.ini: -------------------------------------------------------------------------------- 1 | [line-bot] 2 | channel_access_token = your_channel_access_token 3 | channel_secret = your_channel_secret -------------------------------------------------------------------------------- /D12/config.ini: -------------------------------------------------------------------------------- 1 | [line-bot] 2 | channel_access_token = your_channel_access_token 3 | channel_secret = your_channel_secret -------------------------------------------------------------------------------- /D14/config.ini: -------------------------------------------------------------------------------- 1 | [line-bot] 2 | channel_access_token = your_channel_access_token 3 | channel_secret = your_channel_secret -------------------------------------------------------------------------------- /D17/config.ini: -------------------------------------------------------------------------------- 1 | [line-bot] 2 | channel_access_token = your_channel_access_token 3 | channel_secret = your_channel_secret -------------------------------------------------------------------------------- /D19/config.ini: -------------------------------------------------------------------------------- 1 | [line-bot] 2 | channel_access_token = your_channel_access_token 3 | channel_secret = your_channel_secret -------------------------------------------------------------------------------- /D20/config.ini: -------------------------------------------------------------------------------- 1 | [line-bot] 2 | channel_access_token = your_channel_access_token 3 | channel_secret = your_channel_secret -------------------------------------------------------------------------------- /D22/config.ini: -------------------------------------------------------------------------------- 1 | [line-bot] 2 | channel_access_token = your_channel_access_token 3 | channel_secret = your_channel_secret -------------------------------------------------------------------------------- /D23/config.ini: -------------------------------------------------------------------------------- 1 | [line-bot] 2 | channel_access_token = your_channel_access_token 3 | channel_secret = your_channel_secret -------------------------------------------------------------------------------- /D24/config.ini: -------------------------------------------------------------------------------- 1 | [line-bot] 2 | channel_access_token = your_channel_access_token 3 | channel_secret = your_channel_secret -------------------------------------------------------------------------------- /D17/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.1.1 2 | gunicorn==19.9.0 3 | line-bot-sdk==1.14.0 4 | APScheduler==3.6.1 5 | psycopg2==2.8.3 -------------------------------------------------------------------------------- /D19/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.1.1 2 | gunicorn==19.9.0 3 | line-bot-sdk==1.14.0 4 | APScheduler==3.6.1 5 | psycopg2==2.8.3 -------------------------------------------------------------------------------- /D20/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.1.1 2 | gunicorn==19.9.0 3 | line-bot-sdk==1.14.0 4 | APScheduler==3.6.1 5 | psycopg2==2.8.3 -------------------------------------------------------------------------------- /D22/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.1.1 2 | gunicorn==19.9.0 3 | line-bot-sdk==1.14.0 4 | APScheduler==3.6.1 5 | psycopg2==2.8.3 -------------------------------------------------------------------------------- /D23/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.1.1 2 | gunicorn==19.9.0 3 | line-bot-sdk==1.14.0 4 | APScheduler==3.6.1 5 | psycopg2==2.8.3 -------------------------------------------------------------------------------- /D24/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.1.1 2 | gunicorn==19.9.0 3 | line-bot-sdk==1.14.0 4 | APScheduler==3.6.1 5 | psycopg2==2.8.3 -------------------------------------------------------------------------------- /D20/static/img/alpaca_logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubmhjao/Catcher-in-the-LINE/HEAD/D20/static/img/alpaca_logo.ico -------------------------------------------------------------------------------- /D22/static/img/alpaca_logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubmhjao/Catcher-in-the-LINE/HEAD/D22/static/img/alpaca_logo.ico -------------------------------------------------------------------------------- /D23/static/img/alpaca_logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubmhjao/Catcher-in-the-LINE/HEAD/D23/static/img/alpaca_logo.ico -------------------------------------------------------------------------------- /D24/static/img/alpaca_logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubmhjao/Catcher-in-the-LINE/HEAD/D24/static/img/alpaca_logo.ico -------------------------------------------------------------------------------- /D25/static/img/alpaca_logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubmhjao/Catcher-in-the-LINE/HEAD/D25/static/img/alpaca_logo.ico -------------------------------------------------------------------------------- /D27/static/img/alpaca_logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubmhjao/Catcher-in-the-LINE/HEAD/D27/static/img/alpaca_logo.ico -------------------------------------------------------------------------------- /D29/static/img/alpaca_logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubmhjao/Catcher-in-the-LINE/HEAD/D29/static/img/alpaca_logo.ico -------------------------------------------------------------------------------- /D25/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.1.1 2 | gunicorn==19.9.0 3 | line-bot-sdk==1.14.0 4 | APScheduler==3.6.1 5 | psycopg2==2.8.3 6 | Flask-Login==0.4.1 -------------------------------------------------------------------------------- /D27/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.1.1 2 | gunicorn==19.9.0 3 | line-bot-sdk==1.14.0 4 | APScheduler==3.6.1 5 | psycopg2==2.8.3 6 | Flask-Login==0.4.1 -------------------------------------------------------------------------------- /D29/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.1.1 2 | gunicorn==19.9.0 3 | line-bot-sdk==1.14.0 4 | APScheduler==3.6.1 5 | psycopg2==2.8.3 6 | Flask-Login==0.4.1 -------------------------------------------------------------------------------- /D25/config.ini: -------------------------------------------------------------------------------- 1 | [line-bot] 2 | channel_access_token = your_channel_access_token 3 | channel_secret = your_channel_secret 4 | 5 | [flask] 6 | secret_key = your_secret_key -------------------------------------------------------------------------------- /D27/config.ini: -------------------------------------------------------------------------------- 1 | [line-bot] 2 | channel_access_token = your_channel_access_token 3 | channel_secret = your_channel_secret 4 | 5 | [flask] 6 | secret_key = your_secret_key -------------------------------------------------------------------------------- /D29/config.ini: -------------------------------------------------------------------------------- 1 | [line-bot] 2 | channel_access_token = your_channel_access_token 3 | channel_secret = your_channel_secret 4 | 5 | [flask] 6 | secret_key = your_secret_key -------------------------------------------------------------------------------- /D14/templates/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Simple is BEST 5 | 6 | 7 |

Hello, world!

8 | 9 | -------------------------------------------------------------------------------- /D17/templates/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Simple is BEST 5 | 6 | 7 |

Hello, world!

8 | 9 | -------------------------------------------------------------------------------- /D19/templates/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Simple is BEST 5 | 6 | 7 |

Hello, world!

8 | 9 | -------------------------------------------------------------------------------- /D22/static/css/custom.css: -------------------------------------------------------------------------------- 1 | .bg-dirty-purple {background-color: Indigo;} 2 | .btn-dirty-purple 3 | { 4 | color: #A179BE; 5 | background-color: Indigo; 6 | } 7 | .btn-dirty-purple:hover 8 | { 9 | color: #D2BFE0; 10 | text-decoration: none; 11 | } 12 | .desc {height: 20em;} 13 | .welcome { 14 | height: 40em; 15 | padding: 100px 100px 100px 100px; 16 | } -------------------------------------------------------------------------------- /D23/static/css/custom.css: -------------------------------------------------------------------------------- 1 | .bg-dirty-purple {background-color: Indigo;} 2 | .btn-dirty-purple 3 | { 4 | color: #A179BE; 5 | background-color: Indigo; 6 | } 7 | .btn-dirty-purple:hover 8 | { 9 | color: #D2BFE0; 10 | text-decoration: none; 11 | } 12 | .desc {height: 20em;} 13 | .welcome { 14 | height: 40em; 15 | padding: 100px 100px 100px 100px; 16 | } -------------------------------------------------------------------------------- /D20/templates/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 賴田捕手 7 | 8 | 9 | 10 |

賴田捕手第 20 天

11 |

全身都是肌肉沒半點腦子。反正,那就是鄭尼那晚照顧的草泥馬。我完全無法了解,我發誓我沒辦法。

12 | 13 | -------------------------------------------------------------------------------- /D17/custom_models/CallDatabase.py: -------------------------------------------------------------------------------- 1 | import psycopg2 2 | import os 3 | 4 | def line_insert_record(record_list): 5 | DATABASE_URL = os.environ['DATABASE_URL'] 6 | 7 | conn = psycopg2.connect(DATABASE_URL, sslmode='require') 8 | cursor = conn.cursor() 9 | 10 | table_columns = '(alpaca_name, training, duration, date)' 11 | postgres_insert_query = f"""INSERT INTO alpaca_training {table_columns} VALUES (%s,%s,%s,%s)""" 12 | 13 | cursor.executemany(postgres_insert_query, record_list) 14 | conn.commit() 15 | 16 | message = f"恭喜您! {cursor.rowcount} 筆資料成功匯入 alpaca_training 表單!" 17 | print(message) 18 | 19 | cursor.close() 20 | conn.close() 21 | 22 | return message -------------------------------------------------------------------------------- /D19/custom_models/CallDatabase.py: -------------------------------------------------------------------------------- 1 | import psycopg2 2 | import os 3 | 4 | def line_insert_record(record_list): 5 | DATABASE_URL = os.environ['DATABASE_URL'] 6 | 7 | conn = psycopg2.connect(DATABASE_URL, sslmode='require') 8 | cursor = conn.cursor() 9 | 10 | table_columns = '(alpaca_name, training, duration, date)' 11 | postgres_insert_query = f"""INSERT INTO alpaca_training {table_columns} VALUES (%s,%s,%s,%s)""" 12 | 13 | cursor.executemany(postgres_insert_query, record_list) 14 | conn.commit() 15 | 16 | message = f"恭喜您! {cursor.rowcount} 筆資料成功匯入 alpaca_training 表單!" 17 | print(message) 18 | 19 | cursor.close() 20 | conn.close() 21 | 22 | return message -------------------------------------------------------------------------------- /D20/custom_models/CallDatabase.py: -------------------------------------------------------------------------------- 1 | import psycopg2 2 | import os 3 | 4 | def line_insert_record(record_list): 5 | DATABASE_URL = os.environ['DATABASE_URL'] 6 | 7 | conn = psycopg2.connect(DATABASE_URL, sslmode='require') 8 | cursor = conn.cursor() 9 | 10 | table_columns = '(alpaca_name, training, duration, date)' 11 | postgres_insert_query = f"""INSERT INTO alpaca_training {table_columns} VALUES (%s,%s,%s,%s)""" 12 | 13 | cursor.executemany(postgres_insert_query, record_list) 14 | conn.commit() 15 | 16 | message = f"恭喜您! {cursor.rowcount} 筆資料成功匯入 alpaca_training 表單!" 17 | print(message) 18 | 19 | cursor.close() 20 | conn.close() 21 | 22 | return message -------------------------------------------------------------------------------- /D22/custom_models/CallDatabase.py: -------------------------------------------------------------------------------- 1 | import psycopg2 2 | import os 3 | 4 | def line_insert_record(record_list): 5 | DATABASE_URL = os.environ['DATABASE_URL'] 6 | 7 | conn = psycopg2.connect(DATABASE_URL, sslmode='require') 8 | cursor = conn.cursor() 9 | 10 | table_columns = '(alpaca_name, training, duration, date)' 11 | postgres_insert_query = f"""INSERT INTO alpaca_training {table_columns} VALUES (%s,%s,%s,%s)""" 12 | 13 | cursor.executemany(postgres_insert_query, record_list) 14 | conn.commit() 15 | 16 | message = f"恭喜您! {cursor.rowcount} 筆資料成功匯入 alpaca_training 表單!" 17 | print(message) 18 | 19 | cursor.close() 20 | conn.close() 21 | 22 | return message -------------------------------------------------------------------------------- /D23/custom_models/CallDatabase.py: -------------------------------------------------------------------------------- 1 | import psycopg2 2 | import os 3 | 4 | def line_insert_record(record_list): 5 | DATABASE_URL = os.environ['DATABASE_URL'] 6 | 7 | conn = psycopg2.connect(DATABASE_URL, sslmode='require') 8 | cursor = conn.cursor() 9 | 10 | table_columns = '(alpaca_name, training, duration, date)' 11 | postgres_insert_query = f"""INSERT INTO alpaca_training {table_columns} VALUES (%s,%s,%s,%s)""" 12 | 13 | cursor.executemany(postgres_insert_query, record_list) 14 | conn.commit() 15 | 16 | message = f"恭喜您! {cursor.rowcount} 筆資料成功匯入 alpaca_training 表單!" 17 | print(message) 18 | 19 | cursor.close() 20 | conn.close() 21 | 22 | return message -------------------------------------------------------------------------------- /D10/app_core.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import os 3 | from flask import Flask, request, abort 4 | from linebot import LineBotApi, WebhookHandler 5 | from linebot.exceptions import InvalidSignatureError 6 | 7 | app = Flask(__name__) 8 | 9 | # LINE 聊天機器人的基本資料 10 | line_bot_api = LineBotApi('聊天機器人的 Chennel access token') 11 | handler = WebhookHandler('聊天機器人的 Channel secret') 12 | 13 | # 接收 LINE 的資訊 14 | @app.route("/callback", methods=['POST']) 15 | def callback(): 16 | signature = request.headers['X-Line-Signature'] 17 | 18 | body = request.get_data(as_text=True) 19 | app.logger.info("Request body: " + body) 20 | 21 | try: 22 | handler.handle(body, signature) 23 | except InvalidSignatureError: 24 | abort(400) 25 | 26 | return 'OK' 27 | 28 | if __name__ == "__main__": 29 | app.run() -------------------------------------------------------------------------------- /D22/templates/from_start.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}賴田捕手:重頭開始{% endblock %} 4 | 5 | {% block main %} 6 | 7 |
8 |
9 | 10 |

賴田捕手

11 |

  如果有人真的有興趣聽的話,那麼第一件事情想必是關於我是不是個魯蛇阿宅,不然就是我的身高體重,我平常都吃些什麼,或是家裡眷養了幾隻草尼馬等等不登大雅之堂的問題。不過呢,我並不打算說這些。事實是,我對於討論這些東西一點興趣也沒有。首先得知道的是,草尼馬們並不喜歡被人說三道四的。他們極度纖細敏感,非常重視個人隱私,重視到即使你直直瞪進他們的眼睛,你還是猜不透他們在想些什麼。我並不是在說他們城府很深什麼的,而是因為如果我還不打算進入正題的話,字數限制就要到了。因此,我只準備說一些這半年多來,我從Python、到LINE BOT、到heroku、到資料視覺化,搖搖晃晃跌跌撞撞而又漫無目的的心路歷程。這一路過來,我接受了很多人的幫助,認識或不認識,我都點滴在心。想利用這次鐵人賽的機會,把做一隻LINE BOT的程序有系統的記錄下來,將手頭上好不容易掌握住的東西梳理一遍,也算是權充感謝。關於如何解釋這隻LINE BOT的腳色定位,想了一想,覺得還挺像是個人秘書的。從收集信息,數據處理,到資料視覺化。因此這個鐵人賽的主題,即是從LINE BOT到資料視覺化的30天:賴田捕手。

12 |
13 |
14 | 15 | {% endblock %} 16 | 17 | {% block script %} 18 | 21 | {% endblock %} -------------------------------------------------------------------------------- /D23/templates/from_start.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}賴田捕手:重頭開始{% endblock %} 4 | 5 | {% block main %} 6 | 7 |
8 |
9 | 10 |

賴田捕手

11 |

  如果有人真的有興趣聽的話,那麼第一件事情想必是關於我是不是個魯蛇阿宅,不然就是我的身高體重,我平常都吃些什麼,或是家裡眷養了幾隻草尼馬等等不登大雅之堂的問題。不過呢,我並不打算說這些。事實是,我對於討論這些東西一點興趣也沒有。首先得知道的是,草尼馬們並不喜歡被人說三道四的。他們極度纖細敏感,非常重視個人隱私,重視到即使你直直瞪進他們的眼睛,你還是猜不透他們在想些什麼。我並不是在說他們城府很深什麼的,而是因為如果我還不打算進入正題的話,字數限制就要到了。因此,我只準備說一些這半年多來,我從Python、到LINE BOT、到heroku、到資料視覺化,搖搖晃晃跌跌撞撞而又漫無目的的心路歷程。這一路過來,我接受了很多人的幫助,認識或不認識,我都點滴在心。想利用這次鐵人賽的機會,把做一隻LINE BOT的程序有系統的記錄下來,將手頭上好不容易掌握住的東西梳理一遍,也算是權充感謝。關於如何解釋這隻LINE BOT的腳色定位,想了一想,覺得還挺像是個人秘書的。從收集信息,數據處理,到資料視覺化。因此這個鐵人賽的主題,即是從LINE BOT到資料視覺化的30天:賴田捕手。

12 |
13 |
14 | 15 | {% endblock %} 16 | 17 | {% block script %} 18 | 21 | {% endblock %} -------------------------------------------------------------------------------- /D24/templates/from_start.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}賴田捕手:重頭開始{% endblock %} 4 | 5 | {% block main %} 6 | 7 |
8 |
9 | 10 |

賴田捕手

11 |

  如果有人真的有興趣聽的話,那麼第一件事情想必是關於我是不是個魯蛇阿宅,不然就是我的身高體重,我平常都吃些什麼,或是家裡眷養了幾隻草尼馬等等不登大雅之堂的問題。不過呢,我並不打算說這些。事實是,我對於討論這些東西一點興趣也沒有。首先得知道的是,草尼馬們並不喜歡被人說三道四的。他們極度纖細敏感,非常重視個人隱私,重視到即使你直直瞪進他們的眼睛,你還是猜不透他們在想些什麼。我並不是在說他們城府很深什麼的,而是因為如果我還不打算進入正題的話,字數限制就要到了。因此,我只準備說一些這半年多來,我從Python、到LINE BOT、到heroku、到資料視覺化,搖搖晃晃跌跌撞撞而又漫無目的的心路歷程。這一路過來,我接受了很多人的幫助,認識或不認識,我都點滴在心。想利用這次鐵人賽的機會,把做一隻LINE BOT的程序有系統的記錄下來,將手頭上好不容易掌握住的東西梳理一遍,也算是權充感謝。關於如何解釋這隻LINE BOT的腳色定位,想了一想,覺得還挺像是個人秘書的。從收集信息,數據處理,到資料視覺化。因此這個鐵人賽的主題,即是從LINE BOT到資料視覺化的30天:賴田捕手。

12 |
13 |
14 | 15 | {% endblock %} 16 | 17 | {% block script %} 18 | 21 | {% endblock %} -------------------------------------------------------------------------------- /D23/templates/show_records.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}賴田捕手:訓練紀錄{% endblock %} 4 | 5 | {% block main %} 6 | 7 |
8 |
9 |
10 |

訓練紀錄

11 |
12 |
13 | 14 | 15 | {% macro easy_row(data, tag) -%} 16 | {% for record in data %} 17 |
18 | {% for item in record %} 19 |
20 | <{{ tag }}>{{ item }} 21 |
22 | {% endfor %} 23 |
24 | {% endfor %} 25 | {%- endmacro %} 26 | 27 | 28 | {% set col_names = (("record_no", "alpaca_name", "training", "duration", "date"),) %} 29 | 30 | 31 | {{ easy_row(col_names, "h2") }} 32 | 33 | 34 | {{ easy_row(html_records, "p") }} 35 | 36 |
37 | 38 | {% endblock %} -------------------------------------------------------------------------------- /D11/1_app_core.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import os 3 | from flask import Flask, request, abort 4 | from linebot import LineBotApi, WebhookHandler 5 | from linebot.exceptions import InvalidSignatureError 6 | 7 | import configparser 8 | 9 | app = Flask(__name__) 10 | 11 | # LINE 聊天機器人的基本資料 12 | config = configparser.ConfigParser() 13 | config.read('config.ini') 14 | 15 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 16 | handler = WebhookHandler(config.get('line-bot', 'channel_secret')) 17 | 18 | # 接收 LINE 的資訊 19 | @app.route("/callback", methods=['POST']) 20 | def callback(): 21 | signature = request.headers['X-Line-Signature'] 22 | 23 | body = request.get_data(as_text=True) 24 | app.logger.info("Request body: " + body) 25 | 26 | try: 27 | handler.handle(body, signature) 28 | except InvalidSignatureError: 29 | abort(400) 30 | 31 | return 'OK' 32 | 33 | if __name__ == "__main__": 34 | app.run() -------------------------------------------------------------------------------- /D24/templates/show_records.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}賴田捕手:訓練紀錄{% endblock %} 4 | 5 | {% block main %} 6 | 7 |
8 |
9 |
10 |

訓練紀錄

11 |
12 |
13 | 14 | 15 | {% macro easy_row(data, tag) -%} 16 | {% for record in data %} 17 |
18 | {% for item in record %} 19 |
20 | <{{ tag }}>{{ item }} 21 |
22 | {% endfor %} 23 |
24 | {% endfor %} 25 | {%- endmacro %} 26 | 27 | 28 | {% set col_names = (("record_no", "alpaca_name", "training", "duration", "date"),) %} 29 | 30 | 31 | {{ easy_row(col_names, "h2") }} 32 | 33 |
34 | 35 | 36 | {{ easy_row(html_records, "p") }} 37 | 38 |
39 | 40 |
41 | 42 | {% endblock %} -------------------------------------------------------------------------------- /D25/templates/show_records.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}賴田捕手:訓練紀錄{% endblock %} 4 | 5 | {% block main %} 6 | 7 |
8 |
9 |
10 |

訓練紀錄

11 |
12 |
13 | 14 | 15 | {% macro easy_row(data, tag) -%} 16 | {% for record in data %} 17 |
18 | {% for item in record %} 19 |
20 | <{{ tag }}>{{ item }} 21 |
22 | {% endfor %} 23 |
24 | {% endfor %} 25 | {%- endmacro %} 26 | 27 | 28 | {% set col_names = (("record_no", "alpaca_name", "training", "duration", "date"),) %} 29 | 30 | 31 | {{ easy_row(col_names, "h2") }} 32 | 33 |
34 | 35 | 36 | {{ easy_row(html_records, "p") }} 37 | 38 |
39 | 40 |
41 | 42 | {% endblock %} -------------------------------------------------------------------------------- /D27/templates/show_records.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}賴田捕手:訓練紀錄{% endblock %} 4 | 5 | {% block main %} 6 | 7 |
8 |
9 |
10 |

訓練紀錄

11 |
12 |
13 | 14 | 15 | {% macro easy_row(data, tag) -%} 16 | {% for record in data %} 17 |
18 | {% for item in record %} 19 |
20 | <{{ tag }}>{{ item }} 21 |
22 | {% endfor %} 23 |
24 | {% endfor %} 25 | {%- endmacro %} 26 | 27 | 28 | {% set col_names = (("record_no", "alpaca_name", "training", "duration", "date"),) %} 29 | 30 | 31 | {{ easy_row(col_names, "h2") }} 32 | 33 |
34 | 35 | 36 | {{ easy_row(html_records, "p") }} 37 | 38 |
39 | 40 |
41 | 42 | {% endblock %} -------------------------------------------------------------------------------- /D29/templates/show_records.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}賴田捕手:訓練紀錄{% endblock %} 4 | 5 | {% block main %} 6 | 7 |
8 |
9 |
10 |

訓練紀錄

11 |
12 |
13 | 14 | 15 | {% macro easy_row(data, tag) -%} 16 | {% for record in data %} 17 |
18 | {% for item in record %} 19 |
20 | <{{ tag }}>{{ item }} 21 |
22 | {% endfor %} 23 |
24 | {% endfor %} 25 | {%- endmacro %} 26 | 27 | 28 | {% set col_names = (("record_no", "alpaca_name", "training", "duration", "date"),) %} 29 | 30 | 31 | {{ easy_row(col_names, "h2") }} 32 | 33 |
34 | 35 | 36 | {{ easy_row(html_records, "p") }} 37 | 38 |
39 | 40 |
41 | 42 | {% endblock %} -------------------------------------------------------------------------------- /D22/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}賴田捕手{% endblock %} 4 | 5 | {% block main %} 6 |
7 |

跟《賴田捕手》打聲招呼!

8 |

就像一個巨大的橫幅看板一樣,Jumbotron 可以快速的抓住使用者的目光!

9 |
10 |

當網頁已經設計的差不多,卻還有巨大的空間不知道該怎麼處理時,想想一直待在你身邊的好朋友 Jumbotron

11 | 了解更多 12 |
13 |
14 |
15 |
16 |

輕鬆

17 |

不住在紐約的人可能不知道,維克牧場坐落在一家裝潢的有點華麗的賽頓莊園裡。我以前經常去那裡,但現在我再也不去了。我慢慢沒有了這個習慣。維克牧場是個相當愉快適合社交的場合,令許許多多成熟老練的草泥馬們爭相前往。

18 |
19 |
20 |

愉悅

21 |

有一個在我隔壁的傢伙不停的對自己照顧的草泥馬細聲細氣的說些討好的話。他不停的稱讚著牠的羊蹄,說牠有著貴族一般修長又強韌的羊蹄。什麼鬼。

22 |
23 |
24 |

無害

25 |

我確實問得太過私人了,我明白這點。但這就是露絲讓人覺得厭煩的其中一點。當我們還在胡頓莊園時,露絲老是喜歡要你描述最私人的一些經驗,還要非常詳細,鉅細靡遺那種。但當你開始討論到她的私事時,她整個臉就直接拉下來給你看。

26 |
27 |
28 |
29 | 30 | {% endblock %} -------------------------------------------------------------------------------- /D23/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}賴田捕手{% endblock %} 4 | 5 | {% block main %} 6 |
7 |

跟《賴田捕手》打聲招呼!

8 |

就像一個巨大的橫幅看板一樣,Jumbotron 可以快速的抓住使用者的目光!

9 |
10 |

當網頁已經設計的差不多,卻還有巨大的空間不知道該怎麼處理時,想想一直待在你身邊的好朋友 Jumbotron

11 | 了解更多 12 |
13 |
14 |
15 |
16 |

輕鬆

17 |

不住在紐約的人可能不知道,維克牧場坐落在一家裝潢的有點華麗的賽頓莊園裡。我以前經常去那裡,但現在我再也不去了。我慢慢沒有了這個習慣。維克牧場是個相當愉快適合社交的場合,令許許多多成熟老練的草泥馬們爭相前往。

18 |
19 |
20 |

愉悅

21 |

有一個在我隔壁的傢伙不停的對自己照顧的草泥馬細聲細氣的說些討好的話。他不停的稱讚著牠的羊蹄,說牠有著貴族一般修長又強韌的羊蹄。什麼鬼。

22 |
23 |
24 |

無害

25 |

我確實問得太過私人了,我明白這點。但這就是露絲讓人覺得厭煩的其中一點。當我們還在胡頓莊園時,露絲老是喜歡要你描述最私人的一些經驗,還要非常詳細,鉅細靡遺那種。但當你開始討論到她的私事時,她整個臉就直接拉下來給你看。

26 |
27 |
28 |
29 | 30 | {% endblock %} -------------------------------------------------------------------------------- /D24/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}賴田捕手{% endblock %} 4 | 5 | {% block main %} 6 |
7 |

跟《賴田捕手》打聲招呼!

8 |

就像一個巨大的橫幅看板一樣,Jumbotron 可以快速的抓住使用者的目光!

9 |
10 |

當網頁已經設計的差不多,卻還有巨大的空間不知道該怎麼處理時,想想一直待在你身邊的好朋友 Jumbotron

11 | 了解更多 12 |
13 |
14 |
15 |
16 |

輕鬆

17 |

不住在紐約的人可能不知道,維克牧場坐落在一家裝潢的有點華麗的賽頓莊園裡。我以前經常去那裡,但現在我再也不去了。我慢慢沒有了這個習慣。維克牧場是個相當愉快適合社交的場合,令許許多多成熟老練的草泥馬們爭相前往。

18 |
19 |
20 |

愉悅

21 |

有一個在我隔壁的傢伙不停的對自己照顧的草泥馬細聲細氣的說些討好的話。他不停的稱讚著牠的羊蹄,說牠有著貴族一般修長又強韌的羊蹄。什麼鬼。

22 |
23 |
24 |

無害

25 |

我確實問得太過私人了,我明白這點。但這就是露絲讓人覺得厭煩的其中一點。當我們還在胡頓莊園時,露絲老是喜歡要你描述最私人的一些經驗,還要非常詳細,鉅細靡遺那種。但當你開始討論到她的私事時,她整個臉就直接拉下來給你看。

26 |
27 |
28 |
29 | 30 | {% endblock %} -------------------------------------------------------------------------------- /D25/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}賴田捕手{% endblock %} 4 | 5 | {% block main %} 6 |
7 |

跟《賴田捕手》打聲招呼!

8 |

就像一個巨大的橫幅看板一樣,Jumbotron 可以快速的抓住使用者的目光!

9 |
10 |

當網頁已經設計的差不多,卻還有巨大的空間不知道該怎麼處理時,想想一直待在你身邊的好朋友 Jumbotron

11 | 了解更多 12 |
13 |
14 |
15 |
16 |

輕鬆

17 |

不住在紐約的人可能不知道,維克牧場坐落在一家裝潢的有點華麗的賽頓莊園裡。我以前經常去那裡,但現在我再也不去了。我慢慢沒有了這個習慣。維克牧場是個相當愉快適合社交的場合,令許許多多成熟老練的草泥馬們爭相前往。

18 |
19 |
20 |

愉悅

21 |

有一個在我隔壁的傢伙不停的對自己照顧的草泥馬細聲細氣的說些討好的話。他不停的稱讚著牠的羊蹄,說牠有著貴族一般修長又強韌的羊蹄。什麼鬼。

22 |
23 |
24 |

無害

25 |

我確實問得太過私人了,我明白這點。但這就是露絲讓人覺得厭煩的其中一點。當我們還在胡頓莊園時,露絲老是喜歡要你描述最私人的一些經驗,還要非常詳細,鉅細靡遺那種。但當你開始討論到她的私事時,她整個臉就直接拉下來給你看。

26 |
27 |
28 |
29 | 30 | {% endblock %} -------------------------------------------------------------------------------- /D27/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}賴田捕手{% endblock %} 4 | 5 | {% block main %} 6 |
7 |

跟《賴田捕手》打聲招呼!

8 |

就像一個巨大的橫幅看板一樣,Jumbotron 可以快速的抓住使用者的目光!

9 |
10 |

當網頁已經設計的差不多,卻還有巨大的空間不知道該怎麼處理時,想想一直待在你身邊的好朋友 Jumbotron

11 | 了解更多 12 |
13 |
14 |
15 |
16 |

輕鬆

17 |

不住在紐約的人可能不知道,維克牧場坐落在一家裝潢的有點華麗的賽頓莊園裡。我以前經常去那裡,但現在我再也不去了。我慢慢沒有了這個習慣。維克牧場是個相當愉快適合社交的場合,令許許多多成熟老練的草泥馬們爭相前往。

18 |
19 |
20 |

愉悅

21 |

有一個在我隔壁的傢伙不停的對自己照顧的草泥馬細聲細氣的說些討好的話。他不停的稱讚著牠的羊蹄,說牠有著貴族一般修長又強韌的羊蹄。什麼鬼。

22 |
23 |
24 |

無害

25 |

我確實問得太過私人了,我明白這點。但這就是露絲讓人覺得厭煩的其中一點。當我們還在胡頓莊園時,露絲老是喜歡要你描述最私人的一些經驗,還要非常詳細,鉅細靡遺那種。但當你開始討論到她的私事時,她整個臉就直接拉下來給你看。

26 |
27 |
28 |
29 | 30 | {% endblock %} -------------------------------------------------------------------------------- /D29/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}賴田捕手{% endblock %} 4 | 5 | {% block main %} 6 |
7 |

跟《賴田捕手》打聲招呼!

8 |

就像一個巨大的橫幅看板一樣,Jumbotron 可以快速的抓住使用者的目光!

9 |
10 |

當網頁已經設計的差不多,卻還有巨大的空間不知道該怎麼處理時,想想一直待在你身邊的好朋友 Jumbotron

11 | 了解更多 12 |
13 |
14 |
15 |
16 |

輕鬆

17 |

不住在紐約的人可能不知道,維克牧場坐落在一家裝潢的有點華麗的賽頓莊園裡。我以前經常去那裡,但現在我再也不去了。我慢慢沒有了這個習慣。維克牧場是個相當愉快適合社交的場合,令許許多多成熟老練的草泥馬們爭相前往。

18 |
19 |
20 |

愉悅

21 |

有一個在我隔壁的傢伙不停的對自己照顧的草泥馬細聲細氣的說些討好的話。他不停的稱讚著牠的羊蹄,說牠有著貴族一般修長又強韌的羊蹄。什麼鬼。

22 |
23 |
24 |

無害

25 |

我確實問得太過私人了,我明白這點。但這就是露絲讓人覺得厭煩的其中一點。當我們還在胡頓莊園時,露絲老是喜歡要你描述最私人的一些經驗,還要非常詳細,鉅細靡遺那種。但當你開始討論到她的私事時,她整個臉就直接拉下來給你看。

26 |
27 |
28 |
29 | 30 | {% endblock %} -------------------------------------------------------------------------------- /D25/templates/from_start.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}賴田捕手:重頭開始{% endblock %} 4 | 5 | {% block main %} 6 | 7 | {% with messages = get_flashed_messages() %} 8 | {% if messages %} 9 |
10 | {% for message in messages %} 11 | {{ message }} 12 | {% endfor %} 13 |
14 | {% endif %} 15 | {% endwith %} 16 | 17 |
18 |
19 | 20 |

賴田捕手

21 |

  如果有人真的有興趣聽的話,那麼第一件事情想必是關於我是不是個魯蛇阿宅,不然就是我的身高體重,我平常都吃些什麼,或是家裡眷養了幾隻草尼馬等等不登大雅之堂的問題。不過呢,我並不打算說這些。事實是,我對於討論這些東西一點興趣也沒有。首先得知道的是,草尼馬們並不喜歡被人說三道四的。他們極度纖細敏感,非常重視個人隱私,重視到即使你直直瞪進他們的眼睛,你還是猜不透他們在想些什麼。我並不是在說他們城府很深什麼的,而是因為如果我還不打算進入正題的話,字數限制就要到了。因此,我只準備說一些這半年多來,我從Python、到LINE BOT、到heroku、到資料視覺化,搖搖晃晃跌跌撞撞而又漫無目的的心路歷程。這一路過來,我接受了很多人的幫助,認識或不認識,我都點滴在心。想利用這次鐵人賽的機會,把做一隻LINE BOT的程序有系統的記錄下來,將手頭上好不容易掌握住的東西梳理一遍,也算是權充感謝。關於如何解釋這隻LINE BOT的腳色定位,想了一想,覺得還挺像是個人秘書的。從收集信息,數據處理,到資料視覺化。因此這個鐵人賽的主題,即是從LINE BOT到資料視覺化的30天:賴田捕手。

22 |
23 |
24 | 25 | {% endblock %} 26 | 27 | {% block script %} 28 | 31 | {% endblock %} -------------------------------------------------------------------------------- /D27/templates/from_start.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}賴田捕手:重頭開始{% endblock %} 4 | 5 | {% block main %} 6 | 7 | {% with messages = get_flashed_messages() %} 8 | {% if messages %} 9 |
10 | {% for message in messages %} 11 | {{ message }} 12 | {% endfor %} 13 |
14 | {% endif %} 15 | {% endwith %} 16 | 17 |
18 |
19 | 20 |

賴田捕手

21 |

  如果有人真的有興趣聽的話,那麼第一件事情想必是關於我是不是個魯蛇阿宅,不然就是我的身高體重,我平常都吃些什麼,或是家裡眷養了幾隻草尼馬等等不登大雅之堂的問題。不過呢,我並不打算說這些。事實是,我對於討論這些東西一點興趣也沒有。首先得知道的是,草尼馬們並不喜歡被人說三道四的。他們極度纖細敏感,非常重視個人隱私,重視到即使你直直瞪進他們的眼睛,你還是猜不透他們在想些什麼。我並不是在說他們城府很深什麼的,而是因為如果我還不打算進入正題的話,字數限制就要到了。因此,我只準備說一些這半年多來,我從Python、到LINE BOT、到heroku、到資料視覺化,搖搖晃晃跌跌撞撞而又漫無目的的心路歷程。這一路過來,我接受了很多人的幫助,認識或不認識,我都點滴在心。想利用這次鐵人賽的機會,把做一隻LINE BOT的程序有系統的記錄下來,將手頭上好不容易掌握住的東西梳理一遍,也算是權充感謝。關於如何解釋這隻LINE BOT的腳色定位,想了一想,覺得還挺像是個人秘書的。從收集信息,數據處理,到資料視覺化。因此這個鐵人賽的主題,即是從LINE BOT到資料視覺化的30天:賴田捕手。

22 |
23 |
24 | 25 | {% endblock %} 26 | 27 | {% block script %} 28 | 31 | {% endblock %} -------------------------------------------------------------------------------- /D29/templates/from_start.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}賴田捕手:重頭開始{% endblock %} 4 | 5 | {% block main %} 6 | 7 | {% with messages = get_flashed_messages() %} 8 | {% if messages %} 9 |
10 | {% for message in messages %} 11 | {{ message }} 12 | {% endfor %} 13 |
14 | {% endif %} 15 | {% endwith %} 16 | 17 |
18 |
19 | 20 |

賴田捕手

21 |

  如果有人真的有興趣聽的話,那麼第一件事情想必是關於我是不是個魯蛇阿宅,不然就是我的身高體重,我平常都吃些什麼,或是家裡眷養了幾隻草尼馬等等不登大雅之堂的問題。不過呢,我並不打算說這些。事實是,我對於討論這些東西一點興趣也沒有。首先得知道的是,草尼馬們並不喜歡被人說三道四的。他們極度纖細敏感,非常重視個人隱私,重視到即使你直直瞪進他們的眼睛,你還是猜不透他們在想些什麼。我並不是在說他們城府很深什麼的,而是因為如果我還不打算進入正題的話,字數限制就要到了。因此,我只準備說一些這半年多來,我從Python、到LINE BOT、到heroku、到資料視覺化,搖搖晃晃跌跌撞撞而又漫無目的的心路歷程。這一路過來,我接受了很多人的幫助,認識或不認識,我都點滴在心。想利用這次鐵人賽的機會,把做一隻LINE BOT的程序有系統的記錄下來,將手頭上好不容易掌握住的東西梳理一遍,也算是權充感謝。關於如何解釋這隻LINE BOT的腳色定位,想了一想,覺得還挺像是個人秘書的。從收集信息,數據處理,到資料視覺化。因此這個鐵人賽的主題,即是從LINE BOT到資料視覺化的30天:賴田捕手。

22 |
23 |
24 | 25 | {% endblock %} 26 | 27 | {% block script %} 28 | 31 | {% endblock %} -------------------------------------------------------------------------------- /D10/app_echo.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import os 3 | from flask import Flask, request, abort 4 | from linebot import LineBotApi, WebhookHandler 5 | from linebot.exceptions import InvalidSignatureError 6 | 7 | from linebot.models import MessageEvent, TextMessage, TextSendMessage 8 | 9 | app = Flask(__name__) 10 | 11 | # LINE 聊天機器人的基本資料 12 | line_bot_api = LineBotApi('聊天機器人的 Chennel access token') 13 | handler = WebhookHandler('聊天機器人的 Channel secret') 14 | 15 | 16 | # 接收 LINE 的資訊 17 | @app.route("/callback", methods=['POST']) 18 | def callback(): 19 | signature = request.headers['X-Line-Signature'] 20 | 21 | body = request.get_data(as_text=True) 22 | app.logger.info("Request body: " + body) 23 | 24 | try: 25 | handler.handle(body, signature) 26 | except InvalidSignatureError: 27 | abort(400) 28 | 29 | return 'OK' 30 | 31 | # 學你說話 32 | @handler.add(MessageEvent, message=TextMessage) 33 | def echo(event): 34 | 35 | if event.source.user_id != "Udeadbeefdeadbeefdeadbeefdeadbeef": 36 | line_bot_api.reply_message( 37 | event.reply_token, 38 | TextSendMessage(text=event.message.text) 39 | ) 40 | 41 | if __name__ == "__main__": 42 | app.run() -------------------------------------------------------------------------------- /D11/2_app_echo.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import os 3 | from flask import Flask, request, abort 4 | from linebot import LineBotApi, WebhookHandler 5 | from linebot.exceptions import InvalidSignatureError 6 | from linebot.models import MessageEvent, TextMessage, TextSendMessage 7 | 8 | import configparser 9 | 10 | app = Flask(__name__) 11 | 12 | # LINE 聊天機器人的基本資料 13 | config = configparser.ConfigParser() 14 | config.read('config.ini') 15 | 16 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 17 | handler = WebhookHandler(config.get('line-bot', 'channel_secret')) 18 | 19 | 20 | # 接收 LINE 的資訊 21 | @app.route("/callback", methods=['POST']) 22 | def callback(): 23 | signature = request.headers['X-Line-Signature'] 24 | 25 | body = request.get_data(as_text=True) 26 | app.logger.info("Request body: " + body) 27 | 28 | # 我加了下面那一行 29 | print(body) 30 | # 我加了上面那一行 31 | 32 | try: 33 | handler.handle(body, signature) 34 | except InvalidSignatureError: 35 | abort(400) 36 | 37 | return 'OK' 38 | 39 | # 學你說話 40 | @handler.add(MessageEvent, message=TextMessage) 41 | def echo(event): 42 | 43 | line_bot_api.reply_message( 44 | event.reply_token, 45 | TextSendMessage(text=event.message.text) 46 | ) 47 | 48 | if __name__ == "__main__": 49 | app.run() -------------------------------------------------------------------------------- /D11/3_app_echo.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import os 3 | from flask import Flask, request, abort 4 | from linebot import LineBotApi, WebhookHandler 5 | from linebot.exceptions import InvalidSignatureError 6 | from linebot.models import MessageEvent, TextMessage, TextSendMessage 7 | 8 | import configparser 9 | 10 | app = Flask(__name__) 11 | 12 | # LINE 聊天機器人的基本資料 13 | config = configparser.ConfigParser() 14 | config.read('config.ini') 15 | 16 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 17 | handler = WebhookHandler(config.get('line-bot', 'channel_secret')) 18 | 19 | 20 | # 接收 LINE 的資訊 21 | @app.route("/callback", methods=['POST']) 22 | def callback(): 23 | signature = request.headers['X-Line-Signature'] 24 | 25 | body = request.get_data(as_text=True) 26 | app.logger.info("Request body: " + body) 27 | 28 | try: 29 | handler.handle(body, signature) 30 | except InvalidSignatureError: 31 | abort(400) 32 | 33 | return 'OK' 34 | 35 | # 學你說話 36 | @handler.add(MessageEvent, message=TextMessage) 37 | def echo(event): 38 | 39 | # 這次我加了下面這一行 40 | if event.source.user_id != "Udeadbeefdeadbeefdeadbeefdeadbeef": 41 | # 這次我加了上面這一行 42 | 43 | line_bot_api.reply_message( 44 | event.reply_token, 45 | TextSendMessage(text=event.message.text) 46 | ) 47 | 48 | if __name__ == "__main__": 49 | app.run() -------------------------------------------------------------------------------- /D24/templates/select_records.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}賴田捕手:選擇訓練紀錄{% endblock %} 4 | 5 | {% block main %} 6 | 7 | {% macro easy_text_form(col_name, note) -%} 8 |
9 | 10 |
11 | 12 | {{ note }} 13 |
14 |
15 | {%- endmacro %} 16 | 17 | 18 |
19 |
20 |
21 | 22 |

選擇訓練紀錄

23 |

Postgres Select Query

24 | 25 |
26 | 27 |
SELECT
28 |
*
29 |
FROM
30 |
alpaca_training
31 |
WHERE
32 | 33 | {{ easy_text_form("alpaca_name", "請輸入草泥馬的名字") }} 34 | 35 | {{ easy_text_form("training", "請輸入訓練的名稱") }} 36 | 37 | {{ easy_text_form("date", "請輸入訓練的日期") }} 38 | 39 | 40 | 41 |
42 | 43 |
44 |
45 |
46 | 47 | {% endblock %} -------------------------------------------------------------------------------- /D25/templates/select_records.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}賴田捕手:選擇訓練紀錄{% endblock %} 4 | 5 | {% block main %} 6 | 7 | {% macro easy_text_form(col_name, note) -%} 8 |
9 | 10 |
11 | 12 | {{ note }} 13 |
14 |
15 | {%- endmacro %} 16 | 17 | 18 |
19 |
20 |
21 | 22 |

選擇訓練紀錄

23 |

Postgres Select Query

24 | 25 |
26 | 27 |
SELECT
28 |
*
29 |
FROM
30 |
alpaca_training
31 |
WHERE
32 | 33 | {{ easy_text_form("alpaca_name", "請輸入草泥馬的名字") }} 34 | 35 | {{ easy_text_form("training", "請輸入訓練的名稱") }} 36 | 37 | {{ easy_text_form("date", "請輸入訓練的日期") }} 38 | 39 | 40 | 41 |
42 | 43 |
44 |
45 |
46 | 47 | {% endblock %} -------------------------------------------------------------------------------- /D27/templates/select_records.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}賴田捕手:選擇訓練紀錄{% endblock %} 4 | 5 | {% block main %} 6 | 7 | {% macro easy_text_form(col_name, note) -%} 8 |
9 | 10 |
11 | 12 | {{ note }} 13 |
14 |
15 | {%- endmacro %} 16 | 17 | 18 |
19 |
20 |
21 | 22 |

選擇訓練紀錄

23 |

Postgres Select Query

24 | 25 |
26 | 27 |
SELECT
28 |
*
29 |
FROM
30 |
alpaca_training
31 |
WHERE
32 | 33 | {{ easy_text_form("alpaca_name", "請輸入草泥馬的名字") }} 34 | 35 | {{ easy_text_form("training", "請輸入訓練的名稱") }} 36 | 37 | {{ easy_text_form("date", "請輸入訓練的日期") }} 38 | 39 | 40 | 41 |
42 | 43 |
44 |
45 |
46 | 47 | {% endblock %} -------------------------------------------------------------------------------- /D29/templates/select_records.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}賴田捕手:選擇訓練紀錄{% endblock %} 4 | 5 | {% block main %} 6 | 7 | {% macro easy_text_form(col_name, note) -%} 8 |
9 | 10 |
11 | 12 | {{ note }} 13 |
14 |
15 | {%- endmacro %} 16 | 17 | 18 |
19 |
20 |
21 | 22 |

選擇訓練紀錄

23 |

Postgres Select Query

24 | 25 |
26 | 27 |
SELECT
28 |
*
29 |
FROM
30 |
alpaca_training
31 |
WHERE
32 | 33 | {{ easy_text_form("alpaca_name", "請輸入草泥馬的名字") }} 34 | 35 | {{ easy_text_form("training", "請輸入訓練的名稱") }} 36 | 37 | {{ easy_text_form("date", "請輸入訓練的日期") }} 38 | 39 | 40 | 41 |
42 | 43 |
44 |
45 |
46 | 47 | {% endblock %} -------------------------------------------------------------------------------- /D14/clock.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from linebot import LineBotApi, WebhookHandler 4 | from linebot.exceptions import InvalidSignatureError 5 | from linebot.models import TextSendMessage 6 | 7 | import configparser 8 | 9 | from apscheduler.schedulers.blocking import BlockingScheduler 10 | import urllib 11 | import datetime 12 | 13 | # LINE 聊天機器人的基本資料 14 | config = configparser.ConfigParser() 15 | config.read('config.ini') 16 | 17 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 18 | handler = WebhookHandler(config.get('line-bot', 'channel_secret')) 19 | 20 | sched = BlockingScheduler() 21 | 22 | @sched.scheduled_job('cron', day_of_week='mon-fri', minute='*/20') 23 | def scheduled_job(): 24 | print('========== APScheduler CRON =========') 25 | print('This job runs every weekday */20 min.') 26 | print(f'{datetime.datetime.now().ctime()}') 27 | print('========== APScheduler CRON =========') 28 | 29 | url = "https://你-APP-的名字.herokuapp.com/" 30 | conn = urllib.request.urlopen(url) 31 | 32 | for key, value in conn.getheaders(): 33 | print(key, value) 34 | 35 | 36 | @sched.scheduled_job('cron', day_of_week='mon-fri', hour=6, minute=30) 37 | def scheduled_job(): 38 | print('========== APScheduler CRON =========') 39 | print('This job is run every weekday at 6:30') 40 | print('========== APScheduler CRON =========') 41 | 42 | line_bot_api.push_message(to, TextSendMessage(text=push_text)) 43 | 44 | sched.start() -------------------------------------------------------------------------------- /D17/clock.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from linebot import LineBotApi, WebhookHandler 4 | from linebot.exceptions import InvalidSignatureError 5 | from linebot.models import TextSendMessage 6 | 7 | import configparser 8 | 9 | from apscheduler.schedulers.blocking import BlockingScheduler 10 | import urllib 11 | import datetime 12 | 13 | # LINE 聊天機器人的基本資料 14 | config = configparser.ConfigParser() 15 | config.read('config.ini') 16 | 17 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 18 | handler = WebhookHandler(config.get('line-bot', 'channel_secret')) 19 | 20 | sched = BlockingScheduler() 21 | 22 | @sched.scheduled_job('cron', day_of_week='mon-fri', minute='*/20') 23 | def scheduled_job(): 24 | print('========== APScheduler CRON =========') 25 | print('This job runs every weekday */20 min.') 26 | print(f'{datetime.datetime.now().ctime()}') 27 | print('========== APScheduler CRON =========') 28 | 29 | url = "https://你-APP-的名字.herokuapp.com/" 30 | conn = urllib.request.urlopen(url) 31 | 32 | for key, value in conn.getheaders(): 33 | print(key, value) 34 | 35 | 36 | @sched.scheduled_job('cron', day_of_week='mon-fri', hour=6, minute=30) 37 | def scheduled_job(): 38 | print('========== APScheduler CRON =========') 39 | print('This job is run every weekday at 6:30') 40 | print('========== APScheduler CRON =========') 41 | 42 | line_bot_api.push_message(to, TextSendMessage(text=push_text)) 43 | 44 | sched.start() -------------------------------------------------------------------------------- /D19/clock.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from linebot import LineBotApi, WebhookHandler 4 | from linebot.exceptions import InvalidSignatureError 5 | from linebot.models import TextSendMessage 6 | 7 | import configparser 8 | 9 | from apscheduler.schedulers.blocking import BlockingScheduler 10 | import urllib 11 | import datetime 12 | 13 | # LINE 聊天機器人的基本資料 14 | config = configparser.ConfigParser() 15 | config.read('config.ini') 16 | 17 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 18 | handler = WebhookHandler(config.get('line-bot', 'channel_secret')) 19 | 20 | sched = BlockingScheduler() 21 | 22 | @sched.scheduled_job('cron', day_of_week='mon-fri', minute='*/20') 23 | def scheduled_job(): 24 | print('========== APScheduler CRON =========') 25 | print('This job runs every weekday */20 min.') 26 | print(f'{datetime.datetime.now().ctime()}') 27 | print('========== APScheduler CRON =========') 28 | 29 | url = "https://你-APP-的名字.herokuapp.com/" 30 | conn = urllib.request.urlopen(url) 31 | 32 | for key, value in conn.getheaders(): 33 | print(key, value) 34 | 35 | 36 | @sched.scheduled_job('cron', day_of_week='mon-fri', hour=6, minute=30) 37 | def scheduled_job(): 38 | print('========== APScheduler CRON =========') 39 | print(' This job runs every weekday at 6:30 ') 40 | print('========== APScheduler CRON =========') 41 | 42 | line_bot_api.push_message(to, TextSendMessage(text=push_text)) 43 | 44 | sched.start() -------------------------------------------------------------------------------- /D20/clock.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from linebot import LineBotApi, WebhookHandler 4 | from linebot.exceptions import InvalidSignatureError 5 | from linebot.models import TextSendMessage 6 | 7 | import configparser 8 | 9 | from apscheduler.schedulers.blocking import BlockingScheduler 10 | import urllib 11 | import datetime 12 | 13 | # LINE 聊天機器人的基本資料 14 | config = configparser.ConfigParser() 15 | config.read('config.ini') 16 | 17 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 18 | handler = WebhookHandler(config.get('line-bot', 'channel_secret')) 19 | 20 | sched = BlockingScheduler() 21 | 22 | @sched.scheduled_job('cron', day_of_week='mon-fri', minute='*/20') 23 | def scheduled_job(): 24 | print('========== APScheduler CRON =========') 25 | print('This job runs every weekday */20 min.') 26 | print(f'{datetime.datetime.now().ctime()}') 27 | print('========== APScheduler CRON =========') 28 | 29 | url = "https://你-APP-的名字.herokuapp.com/" 30 | conn = urllib.request.urlopen(url) 31 | 32 | for key, value in conn.getheaders(): 33 | print(key, value) 34 | 35 | 36 | @sched.scheduled_job('cron', day_of_week='mon-fri', hour=6, minute=30) 37 | def scheduled_job(): 38 | print('========== APScheduler CRON =========') 39 | print(' This job runs every weekday at 6:30 ') 40 | print('========== APScheduler CRON =========') 41 | 42 | line_bot_api.push_message(to, TextSendMessage(text=push_text)) 43 | 44 | sched.start() -------------------------------------------------------------------------------- /D22/clock.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from linebot import LineBotApi, WebhookHandler 4 | from linebot.exceptions import InvalidSignatureError 5 | from linebot.models import TextSendMessage 6 | 7 | import configparser 8 | 9 | from apscheduler.schedulers.blocking import BlockingScheduler 10 | import urllib 11 | import datetime 12 | 13 | # LINE 聊天機器人的基本資料 14 | config = configparser.ConfigParser() 15 | config.read('config.ini') 16 | 17 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 18 | handler = WebhookHandler(config.get('line-bot', 'channel_secret')) 19 | 20 | sched = BlockingScheduler() 21 | 22 | @sched.scheduled_job('cron', day_of_week='mon-fri', minute='*/20') 23 | def scheduled_job(): 24 | print('========== APScheduler CRON =========') 25 | print('This job runs every weekday */20 min.') 26 | print(f'{datetime.datetime.now().ctime()}') 27 | print('========== APScheduler CRON =========') 28 | 29 | url = "https://你-APP-的名字.herokuapp.com/" 30 | conn = urllib.request.urlopen(url) 31 | 32 | for key, value in conn.getheaders(): 33 | print(key, value) 34 | 35 | 36 | @sched.scheduled_job('cron', day_of_week='mon-fri', hour=6, minute=30) 37 | def scheduled_job(): 38 | print('========== APScheduler CRON =========') 39 | print(' This job runs every weekday at 6:30 ') 40 | print('========== APScheduler CRON =========') 41 | 42 | line_bot_api.push_message(to, TextSendMessage(text=push_text)) 43 | 44 | sched.start() -------------------------------------------------------------------------------- /D23/clock.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from linebot import LineBotApi, WebhookHandler 4 | from linebot.exceptions import InvalidSignatureError 5 | from linebot.models import TextSendMessage 6 | 7 | import configparser 8 | 9 | from apscheduler.schedulers.blocking import BlockingScheduler 10 | import urllib 11 | import datetime 12 | 13 | # LINE 聊天機器人的基本資料 14 | config = configparser.ConfigParser() 15 | config.read('config.ini') 16 | 17 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 18 | handler = WebhookHandler(config.get('line-bot', 'channel_secret')) 19 | 20 | sched = BlockingScheduler() 21 | 22 | @sched.scheduled_job('cron', day_of_week='mon-fri', minute='*/20') 23 | def scheduled_job(): 24 | print('========== APScheduler CRON =========') 25 | print('This job runs every weekday */20 min.') 26 | print(f'{datetime.datetime.now().ctime()}') 27 | print('========== APScheduler CRON =========') 28 | 29 | url = "https://你-APP-的名字.herokuapp.com/" 30 | conn = urllib.request.urlopen(url) 31 | 32 | for key, value in conn.getheaders(): 33 | print(key, value) 34 | 35 | 36 | @sched.scheduled_job('cron', day_of_week='mon-fri', hour=6, minute=30) 37 | def scheduled_job(): 38 | print('========== APScheduler CRON =========') 39 | print(' This job runs every weekday at 6:30 ') 40 | print('========== APScheduler CRON =========') 41 | 42 | line_bot_api.push_message(to, TextSendMessage(text=push_text)) 43 | 44 | sched.start() -------------------------------------------------------------------------------- /D24/clock.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from linebot import LineBotApi, WebhookHandler 4 | from linebot.exceptions import InvalidSignatureError 5 | from linebot.models import TextSendMessage 6 | 7 | import configparser 8 | 9 | from apscheduler.schedulers.blocking import BlockingScheduler 10 | import urllib 11 | import datetime 12 | 13 | # LINE 聊天機器人的基本資料 14 | config = configparser.ConfigParser() 15 | config.read('config.ini') 16 | 17 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 18 | handler = WebhookHandler(config.get('line-bot', 'channel_secret')) 19 | 20 | sched = BlockingScheduler() 21 | 22 | @sched.scheduled_job('cron', day_of_week='mon-fri', minute='*/20') 23 | def scheduled_job(): 24 | print('========== APScheduler CRON =========') 25 | print('This job runs every weekday */20 min.') 26 | print(f'{datetime.datetime.now().ctime()}') 27 | print('========== APScheduler CRON =========') 28 | 29 | url = "https://你-APP-的名字.herokuapp.com/" 30 | conn = urllib.request.urlopen(url) 31 | 32 | for key, value in conn.getheaders(): 33 | print(key, value) 34 | 35 | 36 | @sched.scheduled_job('cron', day_of_week='mon-fri', hour=6, minute=30) 37 | def scheduled_job(): 38 | print('========== APScheduler CRON =========') 39 | print(' This job runs every weekday at 6:30 ') 40 | print('========== APScheduler CRON =========') 41 | 42 | line_bot_api.push_message(to, TextSendMessage(text=push_text)) 43 | 44 | sched.start() -------------------------------------------------------------------------------- /D25/clock.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from linebot import LineBotApi, WebhookHandler 4 | from linebot.exceptions import InvalidSignatureError 5 | from linebot.models import TextSendMessage 6 | 7 | import configparser 8 | 9 | from apscheduler.schedulers.blocking import BlockingScheduler 10 | import urllib 11 | import datetime 12 | 13 | # LINE 聊天機器人的基本資料 14 | config = configparser.ConfigParser() 15 | config.read('config.ini') 16 | 17 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 18 | handler = WebhookHandler(config.get('line-bot', 'channel_secret')) 19 | 20 | sched = BlockingScheduler() 21 | 22 | @sched.scheduled_job('cron', day_of_week='mon-fri', minute='*/20') 23 | def scheduled_job(): 24 | print('========== APScheduler CRON =========') 25 | print('This job runs every weekday */20 min.') 26 | print(f'{datetime.datetime.now().ctime()}') 27 | print('========== APScheduler CRON =========') 28 | 29 | url = "https://你-APP-的名字.herokuapp.com/" 30 | conn = urllib.request.urlopen(url) 31 | 32 | for key, value in conn.getheaders(): 33 | print(key, value) 34 | 35 | 36 | @sched.scheduled_job('cron', day_of_week='mon-fri', hour=6, minute=30) 37 | def scheduled_job(): 38 | print('========== APScheduler CRON =========') 39 | print(' This job runs every weekday at 6:30 ') 40 | print('========== APScheduler CRON =========') 41 | 42 | line_bot_api.push_message(to, TextSendMessage(text=push_text)) 43 | 44 | sched.start() -------------------------------------------------------------------------------- /D27/clock.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from linebot import LineBotApi, WebhookHandler 4 | from linebot.exceptions import InvalidSignatureError 5 | from linebot.models import TextSendMessage 6 | 7 | import configparser 8 | 9 | from apscheduler.schedulers.blocking import BlockingScheduler 10 | import urllib 11 | import datetime 12 | 13 | # LINE 聊天機器人的基本資料 14 | config = configparser.ConfigParser() 15 | config.read('config.ini') 16 | 17 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 18 | handler = WebhookHandler(config.get('line-bot', 'channel_secret')) 19 | 20 | sched = BlockingScheduler() 21 | 22 | @sched.scheduled_job('cron', day_of_week='mon-fri', minute='*/20') 23 | def scheduled_job(): 24 | print('========== APScheduler CRON =========') 25 | print('This job runs every weekday */20 min.') 26 | print(f'{datetime.datetime.now().ctime()}') 27 | print('========== APScheduler CRON =========') 28 | 29 | url = "https://你-APP-的名字.herokuapp.com/" 30 | conn = urllib.request.urlopen(url) 31 | 32 | for key, value in conn.getheaders(): 33 | print(key, value) 34 | 35 | 36 | @sched.scheduled_job('cron', day_of_week='mon-fri', hour=6, minute=30) 37 | def scheduled_job(): 38 | print('========== APScheduler CRON =========') 39 | print(' This job runs every weekday at 6:30 ') 40 | print('========== APScheduler CRON =========') 41 | 42 | line_bot_api.push_message(to, TextSendMessage(text=push_text)) 43 | 44 | sched.start() -------------------------------------------------------------------------------- /D29/clock.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from linebot import LineBotApi, WebhookHandler 4 | from linebot.exceptions import InvalidSignatureError 5 | from linebot.models import TextSendMessage 6 | 7 | import configparser 8 | 9 | from apscheduler.schedulers.blocking import BlockingScheduler 10 | import urllib 11 | import datetime 12 | 13 | # LINE 聊天機器人的基本資料 14 | config = configparser.ConfigParser() 15 | config.read('config.ini') 16 | 17 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 18 | handler = WebhookHandler(config.get('line-bot', 'channel_secret')) 19 | 20 | sched = BlockingScheduler() 21 | 22 | @sched.scheduled_job('cron', day_of_week='mon-fri', minute='*/20') 23 | def scheduled_job(): 24 | print('========== APScheduler CRON =========') 25 | print('This job runs every weekday */20 min.') 26 | print(f'{datetime.datetime.now().ctime()}') 27 | print('========== APScheduler CRON =========') 28 | 29 | url = "https://你-APP-的名字.herokuapp.com/" 30 | conn = urllib.request.urlopen(url) 31 | 32 | for key, value in conn.getheaders(): 33 | print(key, value) 34 | 35 | 36 | @sched.scheduled_job('cron', day_of_week='mon-fri', hour=6, minute=30) 37 | def scheduled_job(): 38 | print('========== APScheduler CRON =========') 39 | print(' This job runs every weekday at 6:30 ') 40 | print('========== APScheduler CRON =========') 41 | 42 | line_bot_api.push_message(to, TextSendMessage(text=push_text)) 43 | 44 | sched.start() -------------------------------------------------------------------------------- /D24/templates/select_records_comfortable.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}賴田捕手:舒適地選擇訓練紀錄{% endblock %} 4 | 5 | {% block main %} 6 | 7 | {% macro easy_option_form(col_name, col_options) -%} 8 |
9 | 10 |
11 | 17 |
18 |
19 | {%- endmacro %} 20 | 21 | 22 |
23 |
24 |
25 |

選擇訓練紀錄

26 |

Postgres Select Query

27 |
28 |
SELECT
29 |
*
30 |
FROM
31 |
alpaca_training
32 |
WHERE
33 | 34 | {{ easy_option_form("alpaca_name", uniques[0]) }} 35 | 36 | {{ easy_option_form("training", uniques[1]) }} 37 | 38 | {{ easy_option_form("date", uniques[2]) }} 39 | 40 | 41 |
42 |
43 |
44 |
45 | 46 | {% endblock %} -------------------------------------------------------------------------------- /D25/templates/select_records_comfortable.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}賴田捕手:舒適地選擇訓練紀錄{% endblock %} 4 | 5 | {% block main %} 6 | 7 | {% macro easy_option_form(col_name, col_options) -%} 8 |
9 | 10 |
11 | 17 |
18 |
19 | {%- endmacro %} 20 | 21 | 22 |
23 |
24 |
25 |

選擇訓練紀錄

26 |

Postgres Select Query

27 |
28 |
SELECT
29 |
*
30 |
FROM
31 |
alpaca_training
32 |
WHERE
33 | 34 | {{ easy_option_form("alpaca_name", uniques[0]) }} 35 | 36 | {{ easy_option_form("training", uniques[1]) }} 37 | 38 | {{ easy_option_form("date", uniques[2]) }} 39 | 40 | 41 |
42 |
43 |
44 |
45 | 46 | {% endblock %} -------------------------------------------------------------------------------- /D27/templates/select_records_comfortable.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}賴田捕手:舒適地選擇訓練紀錄{% endblock %} 4 | 5 | {% block main %} 6 | 7 | {% macro easy_option_form(col_name, col_options) -%} 8 |
9 | 10 |
11 | 17 |
18 |
19 | {%- endmacro %} 20 | 21 | 22 |
23 |
24 |
25 |

選擇訓練紀錄

26 |

Postgres Select Query

27 |
28 |
SELECT
29 |
*
30 |
FROM
31 |
alpaca_training
32 |
WHERE
33 | 34 | {{ easy_option_form("alpaca_name", uniques[0]) }} 35 | 36 | {{ easy_option_form("training", uniques[1]) }} 37 | 38 | {{ easy_option_form("date", uniques[2]) }} 39 | 40 | 41 |
42 |
43 |
44 |
45 | 46 | {% endblock %} -------------------------------------------------------------------------------- /D29/templates/select_records_comfortable.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}賴田捕手:舒適地選擇訓練紀錄{% endblock %} 4 | 5 | {% block main %} 6 | 7 | {% macro easy_option_form(col_name, col_options) -%} 8 |
9 | 10 |
11 | 17 |
18 |
19 | {%- endmacro %} 20 | 21 | 22 |
23 |
24 |
25 |

選擇訓練紀錄

26 |

Postgres Select Query

27 |
28 |
SELECT
29 |
*
30 |
FROM
31 |
alpaca_training
32 |
WHERE
33 | 34 | {{ easy_option_form("alpaca_name", uniques[0]) }} 35 | 36 | {{ easy_option_form("training", uniques[1]) }} 37 | 38 | {{ easy_option_form("date", uniques[2]) }} 39 | 40 | 41 |
42 |
43 |
44 |
45 | 46 | {% endblock %} -------------------------------------------------------------------------------- /D11/4_app_pretty_echo.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import os 3 | from flask import Flask, request, abort 4 | from linebot import LineBotApi, WebhookHandler 5 | from linebot.exceptions import InvalidSignatureError 6 | from linebot.models import MessageEvent, TextMessage, TextSendMessage 7 | 8 | import configparser 9 | 10 | import random 11 | 12 | app = Flask(__name__) 13 | 14 | # LINE 聊天機器人的基本資料 15 | config = configparser.ConfigParser() 16 | config.read('config.ini') 17 | 18 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 19 | handler = WebhookHandler(config.get('line-bot', 'channel_secret')) 20 | 21 | 22 | # 接收 LINE 的資訊 23 | @app.route("/callback", methods=['POST']) 24 | def callback(): 25 | signature = request.headers['X-Line-Signature'] 26 | 27 | body = request.get_data(as_text=True) 28 | app.logger.info("Request body: " + body) 29 | 30 | try: 31 | handler.handle(body, signature) 32 | except InvalidSignatureError: 33 | abort(400) 34 | 35 | return 'OK' 36 | 37 | # 學你說話 38 | @handler.add(MessageEvent, message=TextMessage) 39 | def pretty_echo(event): 40 | 41 | if event.source.user_id != "Udeadbeefdeadbeefdeadbeefdeadbeef": 42 | 43 | # Phoebe 愛唱歌 44 | pretty_note = '♫♪♬' 45 | pretty_text = '' 46 | 47 | for i in event.message.text: 48 | 49 | pretty_text += i 50 | pretty_text += random.choice(pretty_note) 51 | 52 | line_bot_api.reply_message( 53 | event.reply_token, 54 | TextSendMessage(text=pretty_text) 55 | ) 56 | 57 | if __name__ == "__main__": 58 | app.run() -------------------------------------------------------------------------------- /D25/templates/login.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}賴田捕手:使用者登入{% endblock %} 4 | 5 | {% block flash %} 6 |
7 | {% for message in messages %} 8 | {{ message }} 9 | {% endfor %} 10 |
11 | {% endblock %} 12 | 13 | {% block main %} 14 | 15 | {% with messages = get_flashed_messages() %} 16 | {% if messages %} 17 |
18 | {% for message in messages %} 19 | {{ message }} 20 | {% endfor %} 21 |
22 | {% endif %} 23 | {% endwith %} 24 | 25 | 26 |
27 |
28 |
29 | 30 |

使用者登入

31 | 32 |
33 | 34 |
35 | 36 |
37 | 38 | 提示: Me 39 |
40 |
41 |
42 | 43 |
44 | 45 | 提示: myself 46 |
47 |
48 |
49 |
50 | 51 |
52 |
53 |
54 | 55 |
56 |
57 |
58 | 59 | {% endblock %} -------------------------------------------------------------------------------- /D27/templates/login.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}賴田捕手:使用者登入{% endblock %} 4 | 5 | {% block flash %} 6 |
7 | {% for message in messages %} 8 | {{ message }} 9 | {% endfor %} 10 |
11 | {% endblock %} 12 | 13 | {% block main %} 14 | 15 | {% with messages = get_flashed_messages() %} 16 | {% if messages %} 17 |
18 | {% for message in messages %} 19 | {{ message }} 20 | {% endfor %} 21 |
22 | {% endif %} 23 | {% endwith %} 24 | 25 | 26 |
27 |
28 |
29 | 30 |

使用者登入

31 | 32 |
33 | 34 |
35 | 36 |
37 | 38 | 提示: Me 39 |
40 |
41 |
42 | 43 |
44 | 45 | 提示: myself 46 |
47 |
48 |
49 |
50 | 51 |
52 |
53 |
54 | 55 |
56 |
57 |
58 | 59 | {% endblock %} -------------------------------------------------------------------------------- /D29/templates/login.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}賴田捕手:使用者登入{% endblock %} 4 | 5 | {% block flash %} 6 |
7 | {% for message in messages %} 8 | {{ message }} 9 | {% endfor %} 10 |
11 | {% endblock %} 12 | 13 | {% block main %} 14 | 15 | {% with messages = get_flashed_messages() %} 16 | {% if messages %} 17 |
18 | {% for message in messages %} 19 | {{ message }} 20 | {% endfor %} 21 |
22 | {% endif %} 23 | {% endwith %} 24 | 25 | 26 |
27 |
28 |
29 | 30 |

使用者登入

31 | 32 |
33 | 34 |
35 | 36 |
37 | 38 | 提示: Me 39 |
40 |
41 |
42 | 43 |
44 | 45 | 提示: myself 46 |
47 |
48 |
49 |
50 | 51 |
52 |
53 |
54 | 55 |
56 |
57 |
58 | 59 | {% endblock %} -------------------------------------------------------------------------------- /D17/app_record.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import os 3 | from flask import Flask, request, abort 4 | from linebot import LineBotApi, WebhookHandler 5 | from linebot.exceptions import InvalidSignatureError 6 | from linebot.models import MessageEvent, TextMessage, TextSendMessage 7 | 8 | import configparser 9 | 10 | from custom_models import utils, PhoebeTalks 11 | 12 | app = Flask(__name__) 13 | 14 | # LINE 聊天機器人的基本資料 15 | config = configparser.ConfigParser() 16 | config.read('config.ini') 17 | 18 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 19 | handler = WebhookHandler(config.get('line-bot', 'channel_secret')) 20 | 21 | 22 | # 接收 LINE 的資訊 23 | @app.route("/callback", methods=['POST']) 24 | def callback(): 25 | signature = request.headers['X-Line-Signature'] 26 | 27 | body = request.get_data(as_text=True) 28 | app.logger.info("Request body: " + body) 29 | 30 | try: 31 | handler.handle(body, signature) 32 | except InvalidSignatureError: 33 | abort(400) 34 | 35 | return 'OK' 36 | 37 | # 紀錄資料 38 | @handler.add(MessageEvent, message=TextMessage) 39 | def reply_text_message(event): 40 | 41 | if event.source.user_id != "Udeadbeefdeadbeefdeadbeefdeadbeef": 42 | 43 | reply = False 44 | 45 | if not reply: 46 | reply = PhoebeTalks.insert_record(event) 47 | 48 | if not reply: 49 | reply = PhoebeTalks.img_search(event) 50 | 51 | if not reply: 52 | reply = PhoebeTalks.img_search(event) 53 | 54 | if not reply: 55 | reply = PhoebeTalks.pretty_echo(event) 56 | 57 | 58 | if __name__ == "__main__": 59 | app.run() -------------------------------------------------------------------------------- /D19/custom_models/PhoebeFlex.py: -------------------------------------------------------------------------------- 1 | from linebot import LineBotApi, WebhookHandler 2 | from linebot.exceptions import InvalidSignatureError 3 | from linebot.models import FlexSendMessage 4 | 5 | import configparser 6 | 7 | import re 8 | 9 | # 引入我們的套件 10 | from custom_models import utils 11 | 12 | config = configparser.ConfigParser() 13 | config.read('config.ini') 14 | 15 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 16 | 17 | # LINE 提供的 FlexMessage 範例 18 | sample = { 19 | 'type': 'bubble', 20 | 'direction': 'ltr', 21 | 'hero': { 22 | 'type': 'image', 23 | 'url': 'https://scdn.line-apps.com/n/channel_devcenter/img/fx/01_2_restaurant.png', 24 | 'size': 'full', 25 | 'aspect_ratio': '20:13', 26 | 'aspect_mode': 'cover', 27 | 'action': { 'type': 'postback', 'data': 'sample', 'label': 'sample' } 28 | } 29 | } 30 | 31 | # 依照使用者輸入的訊息傳送 FlexMessage 32 | def img_search_flex(event): 33 | 34 | if re.match("flex", event.message.text.lower()): 35 | 36 | try: 37 | 38 | translate = utils.get_translate(event.message.text[5:]) 39 | random_img_url = utils.get_img_url(img_source='pixabay', target=translate) 40 | 41 | contents = utils.prepare_img_search_flex(event.message.text[5:], translate, random_img_url) 42 | 43 | line_bot_api.reply_message( 44 | event.reply_token, 45 | FlexSendMessage( 46 | alt_text = f'flex {translate}', 47 | contents = contents 48 | ) 49 | ) 50 | 51 | return True 52 | 53 | except: 54 | return False 55 | 56 | else: 57 | return False -------------------------------------------------------------------------------- /D20/custom_models/PhoebeFlex.py: -------------------------------------------------------------------------------- 1 | from linebot import LineBotApi, WebhookHandler 2 | from linebot.exceptions import InvalidSignatureError 3 | from linebot.models import FlexSendMessage 4 | 5 | import configparser 6 | 7 | import re 8 | 9 | # 引入我們的套件 10 | from custom_models import utils 11 | 12 | config = configparser.ConfigParser() 13 | config.read('config.ini') 14 | 15 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 16 | 17 | # LINE 提供的 FlexMessage 範例 18 | sample = { 19 | 'type': 'bubble', 20 | 'direction': 'ltr', 21 | 'hero': { 22 | 'type': 'image', 23 | 'url': 'https://scdn.line-apps.com/n/channel_devcenter/img/fx/01_2_restaurant.png', 24 | 'size': 'full', 25 | 'aspect_ratio': '20:13', 26 | 'aspect_mode': 'cover', 27 | 'action': { 'type': 'postback', 'data': 'sample', 'label': 'sample' } 28 | } 29 | } 30 | 31 | # 依照使用者輸入的訊息傳送 FlexMessage 32 | def img_search_flex(event): 33 | 34 | if re.match("flex", event.message.text.lower()): 35 | 36 | try: 37 | 38 | translate = utils.get_translate(event.message.text[5:]) 39 | random_img_url = utils.get_img_url(img_source='pixabay', target=translate) 40 | 41 | contents = utils.prepare_img_search_flex(event.message.text[5:], translate, random_img_url) 42 | 43 | line_bot_api.reply_message( 44 | event.reply_token, 45 | FlexSendMessage( 46 | alt_text = f'flex {translate}', 47 | contents = contents 48 | ) 49 | ) 50 | 51 | return True 52 | 53 | except: 54 | return False 55 | 56 | else: 57 | return False -------------------------------------------------------------------------------- /D22/custom_models/PhoebeFlex.py: -------------------------------------------------------------------------------- 1 | from linebot import LineBotApi, WebhookHandler 2 | from linebot.exceptions import InvalidSignatureError 3 | from linebot.models import FlexSendMessage 4 | 5 | import configparser 6 | 7 | import re 8 | 9 | # 引入我們的套件 10 | from custom_models import utils 11 | 12 | config = configparser.ConfigParser() 13 | config.read('config.ini') 14 | 15 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 16 | 17 | # LINE 提供的 FlexMessage 範例 18 | sample = { 19 | 'type': 'bubble', 20 | 'direction': 'ltr', 21 | 'hero': { 22 | 'type': 'image', 23 | 'url': 'https://scdn.line-apps.com/n/channel_devcenter/img/fx/01_2_restaurant.png', 24 | 'size': 'full', 25 | 'aspect_ratio': '20:13', 26 | 'aspect_mode': 'cover', 27 | 'action': { 'type': 'postback', 'data': 'sample', 'label': 'sample' } 28 | } 29 | } 30 | 31 | # 依照使用者輸入的訊息傳送 FlexMessage 32 | def img_search_flex(event): 33 | 34 | if re.match("flex", event.message.text.lower()): 35 | 36 | try: 37 | 38 | translate = utils.get_translate(event.message.text[5:]) 39 | random_img_url = utils.get_img_url(img_source='pixabay', target=translate) 40 | 41 | contents = utils.prepare_img_search_flex(event.message.text[5:], translate, random_img_url) 42 | 43 | line_bot_api.reply_message( 44 | event.reply_token, 45 | FlexSendMessage( 46 | alt_text = f'flex {translate}', 47 | contents = contents 48 | ) 49 | ) 50 | 51 | return True 52 | 53 | except: 54 | return False 55 | 56 | else: 57 | return False -------------------------------------------------------------------------------- /D23/custom_models/PhoebeFlex.py: -------------------------------------------------------------------------------- 1 | from linebot import LineBotApi, WebhookHandler 2 | from linebot.exceptions import InvalidSignatureError 3 | from linebot.models import FlexSendMessage 4 | 5 | import configparser 6 | 7 | import re 8 | 9 | # 引入我們的套件 10 | from custom_models import utils 11 | 12 | config = configparser.ConfigParser() 13 | config.read('config.ini') 14 | 15 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 16 | 17 | # LINE 提供的 FlexMessage 範例 18 | sample = { 19 | 'type': 'bubble', 20 | 'direction': 'ltr', 21 | 'hero': { 22 | 'type': 'image', 23 | 'url': 'https://scdn.line-apps.com/n/channel_devcenter/img/fx/01_2_restaurant.png', 24 | 'size': 'full', 25 | 'aspect_ratio': '20:13', 26 | 'aspect_mode': 'cover', 27 | 'action': { 'type': 'postback', 'data': 'sample', 'label': 'sample' } 28 | } 29 | } 30 | 31 | # 依照使用者輸入的訊息傳送 FlexMessage 32 | def img_search_flex(event): 33 | 34 | if re.match("flex", event.message.text.lower()): 35 | 36 | try: 37 | 38 | translate = utils.get_translate(event.message.text[5:]) 39 | random_img_url = utils.get_img_url(img_source='pixabay', target=translate) 40 | 41 | contents = utils.prepare_img_search_flex(event.message.text[5:], translate, random_img_url) 42 | 43 | line_bot_api.reply_message( 44 | event.reply_token, 45 | FlexSendMessage( 46 | alt_text = f'flex {translate}', 47 | contents = contents 48 | ) 49 | ) 50 | 51 | return True 52 | 53 | except: 54 | return False 55 | 56 | else: 57 | return False -------------------------------------------------------------------------------- /D24/custom_models/PhoebeFlex.py: -------------------------------------------------------------------------------- 1 | from linebot import LineBotApi, WebhookHandler 2 | from linebot.exceptions import InvalidSignatureError 3 | from linebot.models import FlexSendMessage 4 | 5 | import configparser 6 | 7 | import re 8 | 9 | # 引入我們的套件 10 | from custom_models import utils 11 | 12 | config = configparser.ConfigParser() 13 | config.read('config.ini') 14 | 15 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 16 | 17 | # LINE 提供的 FlexMessage 範例 18 | sample = { 19 | 'type': 'bubble', 20 | 'direction': 'ltr', 21 | 'hero': { 22 | 'type': 'image', 23 | 'url': 'https://scdn.line-apps.com/n/channel_devcenter/img/fx/01_2_restaurant.png', 24 | 'size': 'full', 25 | 'aspect_ratio': '20:13', 26 | 'aspect_mode': 'cover', 27 | 'action': { 'type': 'postback', 'data': 'sample', 'label': 'sample' } 28 | } 29 | } 30 | 31 | # 依照使用者輸入的訊息傳送 FlexMessage 32 | def img_search_flex(event): 33 | 34 | if re.match("flex", event.message.text.lower()): 35 | 36 | try: 37 | 38 | translate = utils.get_translate(event.message.text[5:]) 39 | random_img_url = utils.get_img_url(img_source='pixabay', target=translate) 40 | 41 | contents = utils.prepare_img_search_flex(event.message.text[5:], translate, random_img_url) 42 | 43 | line_bot_api.reply_message( 44 | event.reply_token, 45 | FlexSendMessage( 46 | alt_text = f'flex {translate}', 47 | contents = contents 48 | ) 49 | ) 50 | 51 | return True 52 | 53 | except: 54 | return False 55 | 56 | else: 57 | return False -------------------------------------------------------------------------------- /D25/custom_models/PhoebeFlex.py: -------------------------------------------------------------------------------- 1 | from linebot import LineBotApi, WebhookHandler 2 | from linebot.exceptions import InvalidSignatureError 3 | from linebot.models import FlexSendMessage 4 | 5 | import configparser 6 | 7 | import re 8 | 9 | # 引入我們的套件 10 | from custom_models import utils 11 | 12 | config = configparser.ConfigParser() 13 | config.read('config.ini') 14 | 15 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 16 | 17 | # LINE 提供的 FlexMessage 範例 18 | sample = { 19 | 'type': 'bubble', 20 | 'direction': 'ltr', 21 | 'hero': { 22 | 'type': 'image', 23 | 'url': 'https://scdn.line-apps.com/n/channel_devcenter/img/fx/01_2_restaurant.png', 24 | 'size': 'full', 25 | 'aspect_ratio': '20:13', 26 | 'aspect_mode': 'cover', 27 | 'action': { 'type': 'postback', 'data': 'sample', 'label': 'sample' } 28 | } 29 | } 30 | 31 | # 依照使用者輸入的訊息傳送 FlexMessage 32 | def img_search_flex(event): 33 | 34 | if re.match("flex", event.message.text.lower()): 35 | 36 | try: 37 | 38 | translate = utils.get_translate(event.message.text[5:]) 39 | random_img_url = utils.get_img_url(img_source='pixabay', target=translate) 40 | 41 | contents = utils.prepare_img_search_flex(event.message.text[5:], translate, random_img_url) 42 | 43 | line_bot_api.reply_message( 44 | event.reply_token, 45 | FlexSendMessage( 46 | alt_text = f'flex {translate}', 47 | contents = contents 48 | ) 49 | ) 50 | 51 | return True 52 | 53 | except: 54 | return False 55 | 56 | else: 57 | return False -------------------------------------------------------------------------------- /D27/custom_models/PhoebeFlex.py: -------------------------------------------------------------------------------- 1 | from linebot import LineBotApi, WebhookHandler 2 | from linebot.exceptions import InvalidSignatureError 3 | from linebot.models import FlexSendMessage 4 | 5 | import configparser 6 | 7 | import re 8 | 9 | # 引入我們的套件 10 | from custom_models import utils 11 | 12 | config = configparser.ConfigParser() 13 | config.read('config.ini') 14 | 15 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 16 | 17 | # LINE 提供的 FlexMessage 範例 18 | sample = { 19 | 'type': 'bubble', 20 | 'direction': 'ltr', 21 | 'hero': { 22 | 'type': 'image', 23 | 'url': 'https://scdn.line-apps.com/n/channel_devcenter/img/fx/01_2_restaurant.png', 24 | 'size': 'full', 25 | 'aspect_ratio': '20:13', 26 | 'aspect_mode': 'cover', 27 | 'action': { 'type': 'postback', 'data': 'sample', 'label': 'sample' } 28 | } 29 | } 30 | 31 | # 依照使用者輸入的訊息傳送 FlexMessage 32 | def img_search_flex(event): 33 | 34 | if re.match("flex", event.message.text.lower()): 35 | 36 | try: 37 | 38 | translate = utils.get_translate(event.message.text[5:]) 39 | random_img_url = utils.get_img_url(img_source='pixabay', target=translate) 40 | 41 | contents = utils.prepare_img_search_flex(event.message.text[5:], translate, random_img_url) 42 | 43 | line_bot_api.reply_message( 44 | event.reply_token, 45 | FlexSendMessage( 46 | alt_text = f'flex {translate}', 47 | contents = contents 48 | ) 49 | ) 50 | 51 | return True 52 | 53 | except: 54 | return False 55 | 56 | else: 57 | return False -------------------------------------------------------------------------------- /D29/custom_models/PhoebeFlex.py: -------------------------------------------------------------------------------- 1 | from linebot import LineBotApi, WebhookHandler 2 | from linebot.exceptions import InvalidSignatureError 3 | from linebot.models import FlexSendMessage 4 | 5 | import configparser 6 | 7 | import re 8 | 9 | # 引入我們的套件 10 | from custom_models import utils 11 | 12 | config = configparser.ConfigParser() 13 | config.read('config.ini') 14 | 15 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 16 | 17 | # LINE 提供的 FlexMessage 範例 18 | sample = { 19 | 'type': 'bubble', 20 | 'direction': 'ltr', 21 | 'hero': { 22 | 'type': 'image', 23 | 'url': 'https://scdn.line-apps.com/n/channel_devcenter/img/fx/01_2_restaurant.png', 24 | 'size': 'full', 25 | 'aspect_ratio': '20:13', 26 | 'aspect_mode': 'cover', 27 | 'action': { 'type': 'postback', 'data': 'sample', 'label': 'sample' } 28 | } 29 | } 30 | 31 | # 依照使用者輸入的訊息傳送 FlexMessage 32 | def img_search_flex(event): 33 | 34 | if re.match("flex", event.message.text.lower()): 35 | 36 | try: 37 | 38 | translate = utils.get_translate(event.message.text[5:]) 39 | random_img_url = utils.get_img_url(img_source='pixabay', target=translate) 40 | 41 | contents = utils.prepare_img_search_flex(event.message.text[5:], translate, random_img_url) 42 | 43 | line_bot_api.reply_message( 44 | event.reply_token, 45 | FlexSendMessage( 46 | alt_text = f'flex {translate}', 47 | contents = contents 48 | ) 49 | ) 50 | 51 | return True 52 | 53 | except: 54 | return False 55 | 56 | else: 57 | return False -------------------------------------------------------------------------------- /D19/app_day_19.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import os 3 | from flask import Flask, request, abort 4 | from linebot import LineBotApi, WebhookHandler 5 | from linebot.exceptions import InvalidSignatureError 6 | from linebot.models import MessageEvent, PostbackEvent, TextMessage, TextSendMessage 7 | 8 | import configparser 9 | 10 | from custom_models import utils, PhoebeTalks, PhoebeFlex 11 | 12 | app = Flask(__name__) 13 | 14 | # LINE 聊天機器人的基本資料 15 | config = configparser.ConfigParser() 16 | config.read('config.ini') 17 | 18 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 19 | handler = WebhookHandler(config.get('line-bot', 'channel_secret')) 20 | 21 | 22 | # 接收 LINE 的資訊 23 | @app.route("/callback", methods=['POST']) 24 | def callback(): 25 | signature = request.headers['X-Line-Signature'] 26 | 27 | body = request.get_data(as_text=True) 28 | app.logger.info("Request body: " + body) 29 | 30 | try: 31 | handler.handle(body, signature) 32 | except InvalidSignatureError: 33 | abort(400) 34 | 35 | return 'OK' 36 | 37 | # 紀錄資料 38 | @handler.add(MessageEvent, message=TextMessage) 39 | def reply_text_message(event): 40 | 41 | if event.source.user_id != "Udeadbeefdeadbeefdeadbeefdeadbeef": 42 | 43 | reply = False 44 | 45 | # 將資料存入表格中 46 | if not reply: 47 | reply = PhoebeTalks.insert_record(event) 48 | 49 | # 發送 FlexMessage 50 | if not reply: 51 | reply = PhoebeFlex.img_search_flex(event) 52 | 53 | # 幫忙上網找圖 54 | if not reply: 55 | reply = PhoebeTalks.img_search(event) 56 | 57 | # 裝飾過的回音機器人 58 | if not reply: 59 | reply = PhoebeTalks.pretty_echo(event) 60 | 61 | 62 | if __name__ == "__main__": 63 | app.run() -------------------------------------------------------------------------------- /D27/templates/donut_chart.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}賴田捕手:甜甜圈圖{% endblock %} 4 | 5 | {% block main %} 6 | 7 |
8 | 9 |
10 |
11 |

甜甜圈圖

12 |
13 |
14 | 15 |
16 |
17 |

alpacaName

18 |
19 |
20 |
21 |

training

22 |
23 |
24 |
25 | 26 |
27 | 28 | {% endblock %} 29 | 30 | {% block script %} 31 | 79 | {% endblock %} -------------------------------------------------------------------------------- /D29/templates/donut_chart.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}賴田捕手:甜甜圈圖{% endblock %} 4 | 5 | {% block main %} 6 | 7 |
8 | 9 |
10 |
11 |

甜甜圈圖

12 |
13 |
14 | 15 |
16 |
17 |

alpacaName

18 |
19 |
20 |
21 |

training

22 |
23 |
24 |
25 | 26 |
27 | 28 | {% endblock %} 29 | 30 | {% block script %} 31 | 79 | {% endblock %} -------------------------------------------------------------------------------- /D20/app_day_20.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import os 3 | from flask import Flask, request, abort, render_template 4 | from linebot import LineBotApi, WebhookHandler 5 | from linebot.exceptions import InvalidSignatureError 6 | from linebot.models import MessageEvent, PostbackEvent, TextMessage, TextSendMessage 7 | 8 | import configparser 9 | 10 | from custom_models import utils, PhoebeTalks, PhoebeFlex 11 | 12 | app = Flask(__name__) 13 | 14 | # LINE 聊天機器人的基本資料 15 | config = configparser.ConfigParser() 16 | config.read('config.ini') 17 | 18 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 19 | handler = WebhookHandler(config.get('line-bot', 'channel_secret')) 20 | 21 | # 幫 "你-APP-的名字.herokuapp.com" 做一個家 22 | @app.route("/") 23 | def home(): 24 | return render_template("home.html") 25 | 26 | # 接收 LINE 的資訊 27 | @app.route("/callback", methods=['POST']) 28 | def callback(): 29 | signature = request.headers['X-Line-Signature'] 30 | 31 | body = request.get_data(as_text=True) 32 | app.logger.info("Request body: " + body) 33 | 34 | try: 35 | handler.handle(body, signature) 36 | except InvalidSignatureError: 37 | abort(400) 38 | 39 | return 'OK' 40 | 41 | # 紀錄資料 42 | @handler.add(MessageEvent, message=TextMessage) 43 | def reply_text_message(event): 44 | 45 | if event.source.user_id != "Udeadbeefdeadbeefdeadbeefdeadbeef": 46 | 47 | reply = False 48 | 49 | # 將資料存入表格中 50 | if not reply: 51 | reply = PhoebeTalks.insert_record(event) 52 | 53 | # 發送 FlexMessage 54 | if not reply: 55 | reply = PhoebeFlex.img_search_flex(event) 56 | 57 | # 幫忙上網找圖 58 | if not reply: 59 | reply = PhoebeTalks.img_search(event) 60 | 61 | # 裝飾過的回音機器人 62 | if not reply: 63 | reply = PhoebeTalks.pretty_echo(event) 64 | 65 | 66 | if __name__ == "__main__": 67 | app.run() -------------------------------------------------------------------------------- /D22/app_day_22.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import os 3 | from flask import Flask, request, abort, render_template 4 | from linebot import LineBotApi, WebhookHandler 5 | from linebot.exceptions import InvalidSignatureError 6 | from linebot.models import MessageEvent, PostbackEvent, TextMessage, TextSendMessage 7 | 8 | import configparser 9 | 10 | from custom_models import utils, PhoebeTalks, PhoebeFlex 11 | 12 | app = Flask(__name__) 13 | 14 | # LINE 聊天機器人的基本資料 15 | config = configparser.ConfigParser() 16 | config.read('config.ini') 17 | 18 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 19 | handler = WebhookHandler(config.get('line-bot', 'channel_secret')) 20 | 21 | @app.route("/") 22 | def home(): 23 | return render_template("home.html") 24 | 25 | @app.route("/from_start") 26 | def from_start(): 27 | return render_template("from_start.html") 28 | 29 | # 接收 LINE 的資訊 30 | @app.route("/callback", methods=['POST']) 31 | def callback(): 32 | signature = request.headers['X-Line-Signature'] 33 | 34 | body = request.get_data(as_text=True) 35 | app.logger.info("Request body: " + body) 36 | 37 | try: 38 | handler.handle(body, signature) 39 | except InvalidSignatureError: 40 | abort(400) 41 | 42 | return 'OK' 43 | 44 | # 處理 MessageEvent 中的 TextMessage 45 | @handler.add(MessageEvent, message=TextMessage) 46 | def reply_text_message(event): 47 | 48 | if event.source.user_id != "Udeadbeefdeadbeefdeadbeefdeadbeef": 49 | 50 | reply = False 51 | 52 | # 將資料存入表格中 53 | if not reply: 54 | reply = PhoebeTalks.insert_record(event) 55 | 56 | # 發送 FlexMessage 57 | if not reply: 58 | reply = PhoebeFlex.img_search_flex(event) 59 | 60 | # 幫忙上網找圖 61 | if not reply: 62 | reply = PhoebeTalks.img_search(event) 63 | 64 | # 裝飾過的回音機器人 65 | if not reply: 66 | reply = PhoebeTalks.pretty_echo(event) 67 | 68 | 69 | if __name__ == "__main__": 70 | app.run() -------------------------------------------------------------------------------- /D23/app_day_23.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import os 3 | from flask import Flask, request, abort, render_template 4 | from linebot import LineBotApi, WebhookHandler 5 | from linebot.exceptions import InvalidSignatureError 6 | from linebot.models import MessageEvent, PostbackEvent, TextMessage, TextSendMessage 7 | 8 | import configparser 9 | 10 | from custom_models import utils, PhoebeTalks, PhoebeFlex 11 | 12 | app = Flask(__name__) 13 | 14 | # LINE 聊天機器人的基本資料 15 | config = configparser.ConfigParser() 16 | config.read('config.ini') 17 | 18 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 19 | handler = WebhookHandler(config.get('line-bot', 'channel_secret')) 20 | 21 | @app.route("/") 22 | def home(): 23 | return render_template("home.html") 24 | 25 | @app.route("/from_start") 26 | def from_start(): 27 | return render_template("from_start.html") 28 | 29 | @app.route("/show_records") 30 | def show_records(): 31 | python_records = CallDatabase.web_select_overall() 32 | return render_template("show_records.html", html_records=python_records) 33 | 34 | 35 | # 接收 LINE 的資訊 36 | @app.route("/callback", methods=['POST']) 37 | def callback(): 38 | signature = request.headers['X-Line-Signature'] 39 | 40 | body = request.get_data(as_text=True) 41 | app.logger.info("Request body: " + body) 42 | 43 | try: 44 | handler.handle(body, signature) 45 | except InvalidSignatureError: 46 | abort(400) 47 | 48 | return 'OK' 49 | 50 | # 處理 MessageEvent 中的 TextMessage 51 | @handler.add(MessageEvent, message=TextMessage) 52 | def reply_text_message(event): 53 | 54 | if event.source.user_id != "Udeadbeefdeadbeefdeadbeefdeadbeef": 55 | 56 | reply = False 57 | 58 | # 將資料存入表格中 59 | if not reply: 60 | reply = PhoebeTalks.insert_record(event) 61 | 62 | # 發送 FlexMessage 63 | if not reply: 64 | reply = PhoebeFlex.img_search_flex(event) 65 | 66 | # 幫忙上網找圖 67 | if not reply: 68 | reply = PhoebeTalks.img_search(event) 69 | 70 | # 裝飾過的回音機器人 71 | if not reply: 72 | reply = PhoebeTalks.pretty_echo(event) 73 | 74 | 75 | if __name__ == "__main__": 76 | app.run() -------------------------------------------------------------------------------- /D19/custom_models/PhoebeTalks.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import os 3 | 4 | from linebot import LineBotApi, WebhookHandler 5 | from linebot.exceptions import InvalidSignatureError 6 | from linebot.models import MessageEvent, TextMessage, TextSendMessage, ImageSendMessage 7 | 8 | import configparser 9 | 10 | import random 11 | 12 | # 我們的函數 13 | from custom_models import utils, CallDatabase 14 | 15 | # LINE 聊天機器人的基本資料 16 | config = configparser.ConfigParser() 17 | config.read('config.ini') 18 | 19 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 20 | 21 | # 請 LINE 聊天機器人幫我們存入訓練資料 22 | def insert_record(event): 23 | 24 | if ('草泥馬訓練紀錄' in event.message.text): 25 | 26 | try: 27 | record_list = utils.prepare_record(event.message.text) 28 | reply = CallDatabase.line_insert_record(record_list) 29 | 30 | line_bot_api.reply_message( 31 | event.reply_token, 32 | TextSendMessage(text=reply) 33 | ) 34 | 35 | except: 36 | line_bot_api.reply_message( 37 | event.reply_token, 38 | TextSendMessage(text='失敗了') 39 | ) 40 | 41 | return True 42 | else: 43 | return False 44 | 45 | # 請 pixabay 幫我們找圖 46 | def img_search(event): 47 | 48 | try: 49 | try: 50 | img_source = event.message.text.split(' ')[0].lower() 51 | target = event.message.text.split(' ')[1] 52 | random_img_url = utils.get_img_url(img_source=img_source, target=target) 53 | 54 | except: 55 | random_img_url = utils.get_img_url(img_source='pixabay', target=event.message.text) 56 | 57 | line_bot_api.reply_message( 58 | event.reply_token, 59 | ImageSendMessage( 60 | original_content_url=random_img_url, 61 | preview_image_url=random_img_url 62 | ) 63 | ) 64 | 65 | return True 66 | 67 | except: 68 | return False 69 | 70 | # 裝飾過後的回音 71 | def pretty_echo(event): 72 | pretty_note = '♫♪♬' 73 | pretty_text = '' 74 | 75 | for i in event.message.text: 76 | 77 | pretty_text += i 78 | pretty_text += f" {random.choice(pretty_note)} " 79 | 80 | line_bot_api.reply_message( 81 | event.reply_token, 82 | TextSendMessage(text=pretty_text) 83 | ) 84 | 85 | return True -------------------------------------------------------------------------------- /D20/custom_models/PhoebeTalks.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import os 3 | 4 | from linebot import LineBotApi, WebhookHandler 5 | from linebot.exceptions import InvalidSignatureError 6 | from linebot.models import MessageEvent, TextMessage, TextSendMessage, ImageSendMessage 7 | 8 | import configparser 9 | 10 | import random 11 | 12 | # 我們的函數 13 | from custom_models import utils, CallDatabase 14 | 15 | # LINE 聊天機器人的基本資料 16 | config = configparser.ConfigParser() 17 | config.read('config.ini') 18 | 19 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 20 | 21 | # 請 LINE 聊天機器人幫我們存入訓練資料 22 | def insert_record(event): 23 | 24 | if ('草泥馬訓練紀錄' in event.message.text): 25 | 26 | try: 27 | record_list = utils.prepare_record(event.message.text) 28 | reply = CallDatabase.line_insert_record(record_list) 29 | 30 | line_bot_api.reply_message( 31 | event.reply_token, 32 | TextSendMessage(text=reply) 33 | ) 34 | 35 | except: 36 | line_bot_api.reply_message( 37 | event.reply_token, 38 | TextSendMessage(text='失敗了') 39 | ) 40 | 41 | return True 42 | else: 43 | return False 44 | 45 | # 請 pixabay 幫我們找圖 46 | def img_search(event): 47 | 48 | try: 49 | try: 50 | img_source = event.message.text.split(' ')[0].lower() 51 | target = event.message.text.split(' ')[1] 52 | random_img_url = utils.get_img_url(img_source=img_source, target=target) 53 | 54 | except: 55 | random_img_url = utils.get_img_url(img_source='pixabay', target=event.message.text) 56 | 57 | line_bot_api.reply_message( 58 | event.reply_token, 59 | ImageSendMessage( 60 | original_content_url=random_img_url, 61 | preview_image_url=random_img_url 62 | ) 63 | ) 64 | 65 | return True 66 | 67 | except: 68 | return False 69 | 70 | # 裝飾過後的回音 71 | def pretty_echo(event): 72 | pretty_note = '♫♪♬' 73 | pretty_text = '' 74 | 75 | for i in event.message.text: 76 | 77 | pretty_text += i 78 | pretty_text += f" {random.choice(pretty_note)} " 79 | 80 | line_bot_api.reply_message( 81 | event.reply_token, 82 | TextSendMessage(text=pretty_text) 83 | ) 84 | 85 | return True -------------------------------------------------------------------------------- /D22/custom_models/PhoebeTalks.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import os 3 | 4 | from linebot import LineBotApi, WebhookHandler 5 | from linebot.exceptions import InvalidSignatureError 6 | from linebot.models import MessageEvent, TextMessage, TextSendMessage, ImageSendMessage 7 | 8 | import configparser 9 | 10 | import random 11 | 12 | # 我們的函數 13 | from custom_models import utils, CallDatabase 14 | 15 | # LINE 聊天機器人的基本資料 16 | config = configparser.ConfigParser() 17 | config.read('config.ini') 18 | 19 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 20 | 21 | # 請 LINE 聊天機器人幫我們存入訓練資料 22 | def insert_record(event): 23 | 24 | if ('草泥馬訓練紀錄' in event.message.text): 25 | 26 | try: 27 | record_list = utils.prepare_record(event.message.text) 28 | reply = CallDatabase.line_insert_record(record_list) 29 | 30 | line_bot_api.reply_message( 31 | event.reply_token, 32 | TextSendMessage(text=reply) 33 | ) 34 | 35 | except: 36 | line_bot_api.reply_message( 37 | event.reply_token, 38 | TextSendMessage(text='失敗了') 39 | ) 40 | 41 | return True 42 | else: 43 | return False 44 | 45 | # 請 pixabay 幫我們找圖 46 | def img_search(event): 47 | 48 | try: 49 | try: 50 | img_source = event.message.text.split(' ')[0].lower() 51 | target = event.message.text.split(' ')[1] 52 | random_img_url = utils.get_img_url(img_source=img_source, target=target) 53 | 54 | except: 55 | random_img_url = utils.get_img_url(img_source='pixabay', target=event.message.text) 56 | 57 | line_bot_api.reply_message( 58 | event.reply_token, 59 | ImageSendMessage( 60 | original_content_url=random_img_url, 61 | preview_image_url=random_img_url 62 | ) 63 | ) 64 | 65 | return True 66 | 67 | except: 68 | return False 69 | 70 | # 裝飾過後的回音 71 | def pretty_echo(event): 72 | pretty_note = '♫♪♬' 73 | pretty_text = '' 74 | 75 | for i in event.message.text: 76 | 77 | pretty_text += i 78 | pretty_text += f" {random.choice(pretty_note)} " 79 | 80 | line_bot_api.reply_message( 81 | event.reply_token, 82 | TextSendMessage(text=pretty_text) 83 | ) 84 | 85 | return True -------------------------------------------------------------------------------- /D23/custom_models/PhoebeTalks.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import os 3 | 4 | from linebot import LineBotApi, WebhookHandler 5 | from linebot.exceptions import InvalidSignatureError 6 | from linebot.models import MessageEvent, TextMessage, TextSendMessage, ImageSendMessage 7 | 8 | import configparser 9 | 10 | import random 11 | 12 | # 我們的函數 13 | from custom_models import utils, CallDatabase 14 | 15 | # LINE 聊天機器人的基本資料 16 | config = configparser.ConfigParser() 17 | config.read('config.ini') 18 | 19 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 20 | 21 | # 請 LINE 聊天機器人幫我們存入訓練資料 22 | def insert_record(event): 23 | 24 | if ('草泥馬訓練紀錄' in event.message.text): 25 | 26 | try: 27 | record_list = utils.prepare_record(event.message.text) 28 | reply = CallDatabase.line_insert_record(record_list) 29 | 30 | line_bot_api.reply_message( 31 | event.reply_token, 32 | TextSendMessage(text=reply) 33 | ) 34 | 35 | except: 36 | line_bot_api.reply_message( 37 | event.reply_token, 38 | TextSendMessage(text='失敗了') 39 | ) 40 | 41 | return True 42 | else: 43 | return False 44 | 45 | # 請 pixabay 幫我們找圖 46 | def img_search(event): 47 | 48 | try: 49 | try: 50 | img_source = event.message.text.split(' ')[0].lower() 51 | target = event.message.text.split(' ')[1] 52 | random_img_url = utils.get_img_url(img_source=img_source, target=target) 53 | 54 | except: 55 | random_img_url = utils.get_img_url(img_source='pixabay', target=event.message.text) 56 | 57 | line_bot_api.reply_message( 58 | event.reply_token, 59 | ImageSendMessage( 60 | original_content_url=random_img_url, 61 | preview_image_url=random_img_url 62 | ) 63 | ) 64 | 65 | return True 66 | 67 | except: 68 | return False 69 | 70 | # 裝飾過後的回音 71 | def pretty_echo(event): 72 | pretty_note = '♫♪♬' 73 | pretty_text = '' 74 | 75 | for i in event.message.text: 76 | 77 | pretty_text += i 78 | pretty_text += f" {random.choice(pretty_note)} " 79 | 80 | line_bot_api.reply_message( 81 | event.reply_token, 82 | TextSendMessage(text=pretty_text) 83 | ) 84 | 85 | return True -------------------------------------------------------------------------------- /D24/custom_models/PhoebeTalks.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import os 3 | 4 | from linebot import LineBotApi, WebhookHandler 5 | from linebot.exceptions import InvalidSignatureError 6 | from linebot.models import MessageEvent, TextMessage, TextSendMessage, ImageSendMessage 7 | 8 | import configparser 9 | 10 | import random 11 | 12 | # 我們的函數 13 | from custom_models import utils, CallDatabase 14 | 15 | # LINE 聊天機器人的基本資料 16 | config = configparser.ConfigParser() 17 | config.read('config.ini') 18 | 19 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 20 | 21 | # 請 LINE 聊天機器人幫我們存入訓練資料 22 | def insert_record(event): 23 | 24 | if ('草泥馬訓練紀錄' in event.message.text): 25 | 26 | try: 27 | record_list = utils.prepare_record(event.message.text) 28 | reply = CallDatabase.line_insert_record(record_list) 29 | 30 | line_bot_api.reply_message( 31 | event.reply_token, 32 | TextSendMessage(text=reply) 33 | ) 34 | 35 | except: 36 | line_bot_api.reply_message( 37 | event.reply_token, 38 | TextSendMessage(text='失敗了') 39 | ) 40 | 41 | return True 42 | else: 43 | return False 44 | 45 | # 請 pixabay 幫我們找圖 46 | def img_search(event): 47 | 48 | try: 49 | try: 50 | img_source = event.message.text.split(' ')[0].lower() 51 | target = event.message.text.split(' ')[1] 52 | random_img_url = utils.get_img_url(img_source=img_source, target=target) 53 | 54 | except: 55 | random_img_url = utils.get_img_url(img_source='pixabay', target=event.message.text) 56 | 57 | line_bot_api.reply_message( 58 | event.reply_token, 59 | ImageSendMessage( 60 | original_content_url=random_img_url, 61 | preview_image_url=random_img_url 62 | ) 63 | ) 64 | 65 | return True 66 | 67 | except: 68 | return False 69 | 70 | # 裝飾過後的回音 71 | def pretty_echo(event): 72 | pretty_note = '♫♪♬' 73 | pretty_text = '' 74 | 75 | for i in event.message.text: 76 | 77 | pretty_text += i 78 | pretty_text += f" {random.choice(pretty_note)} " 79 | 80 | line_bot_api.reply_message( 81 | event.reply_token, 82 | TextSendMessage(text=pretty_text) 83 | ) 84 | 85 | return True -------------------------------------------------------------------------------- /D25/custom_models/PhoebeTalks.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import os 3 | 4 | from linebot import LineBotApi, WebhookHandler 5 | from linebot.exceptions import InvalidSignatureError 6 | from linebot.models import MessageEvent, TextMessage, TextSendMessage, ImageSendMessage 7 | 8 | import configparser 9 | 10 | import random 11 | 12 | # 我們的函數 13 | from custom_models import utils, CallDatabase 14 | 15 | # LINE 聊天機器人的基本資料 16 | config = configparser.ConfigParser() 17 | config.read('config.ini') 18 | 19 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 20 | 21 | # 請 LINE 聊天機器人幫我們存入訓練資料 22 | def insert_record(event): 23 | 24 | if ('草泥馬訓練紀錄' in event.message.text): 25 | 26 | try: 27 | record_list = utils.prepare_record(event.message.text) 28 | reply = CallDatabase.line_insert_record(record_list) 29 | 30 | line_bot_api.reply_message( 31 | event.reply_token, 32 | TextSendMessage(text=reply) 33 | ) 34 | 35 | except: 36 | line_bot_api.reply_message( 37 | event.reply_token, 38 | TextSendMessage(text='失敗了') 39 | ) 40 | 41 | return True 42 | else: 43 | return False 44 | 45 | # 請 pixabay 幫我們找圖 46 | def img_search(event): 47 | 48 | try: 49 | try: 50 | img_source = event.message.text.split(' ')[0].lower() 51 | target = event.message.text.split(' ')[1] 52 | random_img_url = utils.get_img_url(img_source=img_source, target=target) 53 | 54 | except: 55 | random_img_url = utils.get_img_url(img_source='pixabay', target=event.message.text) 56 | 57 | line_bot_api.reply_message( 58 | event.reply_token, 59 | ImageSendMessage( 60 | original_content_url=random_img_url, 61 | preview_image_url=random_img_url 62 | ) 63 | ) 64 | 65 | return True 66 | 67 | except: 68 | return False 69 | 70 | # 裝飾過後的回音 71 | def pretty_echo(event): 72 | pretty_note = '♫♪♬' 73 | pretty_text = '' 74 | 75 | for i in event.message.text: 76 | 77 | pretty_text += i 78 | pretty_text += f" {random.choice(pretty_note)} " 79 | 80 | line_bot_api.reply_message( 81 | event.reply_token, 82 | TextSendMessage(text=pretty_text) 83 | ) 84 | 85 | return True -------------------------------------------------------------------------------- /D27/custom_models/PhoebeTalks.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import os 3 | 4 | from linebot import LineBotApi, WebhookHandler 5 | from linebot.exceptions import InvalidSignatureError 6 | from linebot.models import MessageEvent, TextMessage, TextSendMessage, ImageSendMessage 7 | 8 | import configparser 9 | 10 | import random 11 | 12 | # 我們的函數 13 | from custom_models import utils, CallDatabase 14 | 15 | # LINE 聊天機器人的基本資料 16 | config = configparser.ConfigParser() 17 | config.read('config.ini') 18 | 19 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 20 | 21 | # 請 LINE 聊天機器人幫我們存入訓練資料 22 | def insert_record(event): 23 | 24 | if ('草泥馬訓練紀錄' in event.message.text): 25 | 26 | try: 27 | record_list = utils.prepare_record(event.message.text) 28 | reply = CallDatabase.line_insert_record(record_list) 29 | 30 | line_bot_api.reply_message( 31 | event.reply_token, 32 | TextSendMessage(text=reply) 33 | ) 34 | 35 | except: 36 | line_bot_api.reply_message( 37 | event.reply_token, 38 | TextSendMessage(text='失敗了') 39 | ) 40 | 41 | return True 42 | else: 43 | return False 44 | 45 | # 請 pixabay 幫我們找圖 46 | def img_search(event): 47 | 48 | try: 49 | try: 50 | img_source = event.message.text.split(' ')[0].lower() 51 | target = event.message.text.split(' ')[1] 52 | random_img_url = utils.get_img_url(img_source=img_source, target=target) 53 | 54 | except: 55 | random_img_url = utils.get_img_url(img_source='pixabay', target=event.message.text) 56 | 57 | line_bot_api.reply_message( 58 | event.reply_token, 59 | ImageSendMessage( 60 | original_content_url=random_img_url, 61 | preview_image_url=random_img_url 62 | ) 63 | ) 64 | 65 | return True 66 | 67 | except: 68 | return False 69 | 70 | # 裝飾過後的回音 71 | def pretty_echo(event): 72 | pretty_note = '♫♪♬' 73 | pretty_text = '' 74 | 75 | for i in event.message.text: 76 | 77 | pretty_text += i 78 | pretty_text += f" {random.choice(pretty_note)} " 79 | 80 | line_bot_api.reply_message( 81 | event.reply_token, 82 | TextSendMessage(text=pretty_text) 83 | ) 84 | 85 | return True -------------------------------------------------------------------------------- /D29/custom_models/PhoebeTalks.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import os 3 | 4 | from linebot import LineBotApi, WebhookHandler 5 | from linebot.exceptions import InvalidSignatureError 6 | from linebot.models import MessageEvent, TextMessage, TextSendMessage, ImageSendMessage 7 | 8 | import configparser 9 | 10 | import random 11 | 12 | # 我們的函數 13 | from custom_models import utils, CallDatabase 14 | 15 | # LINE 聊天機器人的基本資料 16 | config = configparser.ConfigParser() 17 | config.read('config.ini') 18 | 19 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 20 | 21 | # 請 LINE 聊天機器人幫我們存入訓練資料 22 | def insert_record(event): 23 | 24 | if ('草泥馬訓練紀錄' in event.message.text): 25 | 26 | try: 27 | record_list = utils.prepare_record(event.message.text) 28 | reply = CallDatabase.line_insert_record(record_list) 29 | 30 | line_bot_api.reply_message( 31 | event.reply_token, 32 | TextSendMessage(text=reply) 33 | ) 34 | 35 | except: 36 | line_bot_api.reply_message( 37 | event.reply_token, 38 | TextSendMessage(text='失敗了') 39 | ) 40 | 41 | return True 42 | else: 43 | return False 44 | 45 | # 請 pixabay 幫我們找圖 46 | def img_search(event): 47 | 48 | try: 49 | try: 50 | img_source = event.message.text.split(' ')[0].lower() 51 | target = event.message.text.split(' ')[1] 52 | random_img_url = utils.get_img_url(img_source=img_source, target=target) 53 | 54 | except: 55 | random_img_url = utils.get_img_url(img_source='pixabay', target=event.message.text) 56 | 57 | line_bot_api.reply_message( 58 | event.reply_token, 59 | ImageSendMessage( 60 | original_content_url=random_img_url, 61 | preview_image_url=random_img_url 62 | ) 63 | ) 64 | 65 | return True 66 | 67 | except: 68 | return False 69 | 70 | # 裝飾過後的回音 71 | def pretty_echo(event): 72 | pretty_note = '♫♪♬' 73 | pretty_text = '' 74 | 75 | for i in event.message.text: 76 | 77 | pretty_text += i 78 | pretty_text += f" {random.choice(pretty_note)} " 79 | 80 | line_bot_api.reply_message( 81 | event.reply_token, 82 | TextSendMessage(text=pretty_text) 83 | ) 84 | 85 | return True -------------------------------------------------------------------------------- /D17/custom_models/PhoebeTalks.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import os 3 | 4 | from linebot import LineBotApi, WebhookHandler 5 | from linebot.exceptions import InvalidSignatureError 6 | from linebot.models import MessageEvent, PostbackEvent, TextMessage, TextSendMessage, ImageSendMessage, FlexSendMessage 7 | 8 | import configparser 9 | 10 | import random 11 | 12 | # 我們的函數 13 | from custom_models import utils, CallDatabase 14 | 15 | # LINE 聊天機器人的基本資料 16 | config = configparser.ConfigParser() 17 | config.read('config.ini') 18 | 19 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 20 | 21 | # 請 LINE 幫我們存入資料 22 | def insert_record(event): 23 | 24 | if '草泥馬訓練紀錄' in event.message.text: 25 | 26 | try: 27 | record_list = utils.prepare_record(event.message.text) 28 | reply = CallDatabase.line_insert_record(record_list) 29 | 30 | line_bot_api.reply_message( 31 | event.reply_token, 32 | TextSendMessage(text=reply) 33 | ) 34 | 35 | except: 36 | line_bot_api.reply_message( 37 | event.reply_token, 38 | TextSendMessage(text='失敗了') 39 | ) 40 | 41 | return True 42 | else: 43 | return False 44 | 45 | # 請 pixabay 幫我們找圖 46 | def img_search(event): 47 | 48 | try: 49 | try: 50 | img_source = event.message.text.split(' ')[0].lower() 51 | target = event.message.text.split(' ')[1] 52 | random_img_url = utils.get_img_url(img_source=img_source, target=target) 53 | 54 | except: 55 | random_img_url = utils.get_img_url(img_source='pixabay', target=event.message.text) 56 | 57 | line_bot_api.reply_message( 58 | event.reply_token, 59 | ImageSendMessage( 60 | original_content_url=random_img_url, 61 | preview_image_url=random_img_url 62 | ) 63 | ) 64 | 65 | return True 66 | 67 | except: 68 | return False 69 | 70 | def pretty_echo(event): 71 | pretty_note = '♫♪♬' 72 | pretty_text = '' 73 | 74 | for i in event.message.text: 75 | 76 | pretty_text += i 77 | pretty_text += f" {random.choice(pretty_note)} " 78 | 79 | line_bot_api.reply_message( 80 | event.reply_token, 81 | TextSendMessage(text=pretty_text) 82 | ) 83 | 84 | return True -------------------------------------------------------------------------------- /D17/custom_models/utils.py: -------------------------------------------------------------------------------- 1 | import urllib 2 | import re 3 | import random 4 | import datetime 5 | 6 | # 請 pixabay 幫我們找圖 7 | def get_img_url(img_source, target): 8 | 9 | img_source_dict = { 10 | 'google': [f"https://www.google.com/search?{urllib.parse.urlencode(dict([['tbm', 'isch'], ['q', target]]))}/", 11 | 'img data-src="\S*"', 12 | 14, 13 | -1], 14 | 'pixabay': [f"https://pixabay.com/images/search/{urllib.parse.urlencode(dict([['q', target]]))[2:]}/", 15 | 'img srcset="\S*\s\w*,', 16 | 12, 17 | -3], 18 | 'unsplash': [f"https://unsplash.com/s/photos/{urllib.parse.urlencode(dict([['q', target]]))[2:]}/", 19 | 'srcSet="\S* ', 20 | 8, 21 | -1]} 22 | 23 | url = img_source_dict[img_source][0] 24 | headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36'} 25 | 26 | req = urllib.request.Request(url, headers = headers) 27 | conn = urllib.request.urlopen(req) 28 | 29 | print('fetch page finish') 30 | 31 | img_list = [] 32 | 33 | for match in re.finditer(img_source_dict[img_source][1], str(conn.read())): 34 | img_list.append(match.group()[img_source_dict[img_source][2]:img_source_dict[img_source][3]]) 35 | 36 | random_img_url = random.choice(img_list) 37 | print('fetch img url finish') 38 | print(random_img_url) 39 | 40 | return random_img_url 41 | 42 | 43 | 44 | def prepare_record(text): 45 | text_list = text.split('\n') 46 | 47 | month = text_list[0].split(' ')[0].split('/')[0] 48 | day = text_list[0].split(' ')[0].split('/')[1] 49 | d = datetime.date(datetime.date.today().year, int(month), int(day)) 50 | 51 | record_list = [] 52 | 53 | time_format = '%H:%M' 54 | 55 | for i in text_list[1:]: 56 | temp_list = i.split(' ') 57 | 58 | temp_name = temp_list[0] 59 | temp_training = temp_list[1] 60 | 61 | temp_start = datetime.datetime.strptime(temp_list[2].split('-')[0], time_format) 62 | temp_end = datetime.datetime.strptime(temp_list[2].split('-')[1], time_format) 63 | temp_duration = temp_end - temp_start 64 | 65 | record = (temp_name, temp_training, temp_duration, d) 66 | record_list.append(record) 67 | 68 | return record_list -------------------------------------------------------------------------------- /D12/app_pixabay.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import os 3 | from flask import Flask, request, abort 4 | from linebot import LineBotApi, WebhookHandler 5 | from linebot.exceptions import InvalidSignatureError 6 | from linebot.models import MessageEvent, TextMessage, TextSendMessage, ImageSendMessage 7 | 8 | import configparser 9 | 10 | import urllib 11 | import re 12 | import random 13 | 14 | app = Flask(__name__) 15 | 16 | # LINE 聊天機器人的基本資料 17 | config = configparser.ConfigParser() 18 | config.read('config.ini') 19 | 20 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 21 | handler = WebhookHandler(config.get('line-bot', 'channel_secret')) 22 | 23 | 24 | # 接收 LINE 的資訊 25 | @app.route("/callback", methods=['POST']) 26 | def callback(): 27 | signature = request.headers['X-Line-Signature'] 28 | 29 | body = request.get_data(as_text=True) 30 | app.logger.info("Request body: " + body) 31 | 32 | try: 33 | handler.handle(body, signature) 34 | except InvalidSignatureError: 35 | abort(400) 36 | 37 | return 'OK' 38 | 39 | # 請 pixabay 幫我們找圖 40 | @handler.add(MessageEvent, message=TextMessage) 41 | def pixabay_isch(event): 42 | 43 | if event.source.user_id != "Udeadbeefdeadbeefdeadbeefdeadbeef": 44 | # 先找圖 45 | try: 46 | url = f"https://pixabay.com/images/search/{urllib.parse.urlencode({'q':event.message.text})[2:]}/" 47 | headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36'} 48 | 49 | req = urllib.request.Request(url, headers = headers) 50 | conn = urllib.request.urlopen(req) 51 | 52 | print('fetch page finish') 53 | 54 | pattern = 'img srcset="\S*\s\w*,' 55 | img_list = [] 56 | 57 | for match in re.finditer(pattern, str(conn.read())): 58 | img_list.append(match.group()[12:-3]) 59 | 60 | random_img_url = img_list[random.randint(0, len(img_list)+1)] 61 | print('fetch img url finish') 62 | print(random_img_url) 63 | 64 | line_bot_api.reply_message( 65 | event.reply_token, 66 | ImageSendMessage( 67 | original_content_url=random_img_url, 68 | preview_image_url=random_img_url 69 | ) 70 | ) 71 | # 找不到圖就學說話 72 | except: 73 | line_bot_api.reply_message( 74 | event.reply_token, 75 | TextSendMessage(text=event.message.text) 76 | ) 77 | pass 78 | 79 | if __name__ == "__main__": 80 | app.run() -------------------------------------------------------------------------------- /D12/app_google_isch.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import os 3 | from flask import Flask, request, abort 4 | from linebot import LineBotApi, WebhookHandler 5 | from linebot.exceptions import InvalidSignatureError 6 | from linebot.models import MessageEvent, TextMessage, TextSendMessage, ImageSendMessage 7 | 8 | import configparser 9 | 10 | import urllib 11 | import re 12 | import random 13 | 14 | app = Flask(__name__) 15 | 16 | # LINE 聊天機器人的基本資料 17 | config = configparser.ConfigParser() 18 | config.read('config.ini') 19 | 20 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 21 | handler = WebhookHandler(config.get('line-bot', 'channel_secret')) 22 | 23 | 24 | # 接收 LINE 的資訊 25 | @app.route("/callback", methods=['POST']) 26 | def callback(): 27 | signature = request.headers['X-Line-Signature'] 28 | 29 | body = request.get_data(as_text=True) 30 | app.logger.info("Request body: " + body) 31 | 32 | try: 33 | handler.handle(body, signature) 34 | except InvalidSignatureError: 35 | abort(400) 36 | 37 | return 'OK' 38 | 39 | # 幫你在 google 上找圖 40 | @handler.add(MessageEvent, message=TextMessage) 41 | def google_isch(event): 42 | 43 | if event.source.user_id != "Udeadbeefdeadbeefdeadbeefdeadbeef": 44 | # 先找圖 45 | try: 46 | q_string = {'tbm': 'isch', 'q': event.message.text} 47 | url = f"https://www.google.com/search?{urllib.parse.urlencode(q_string)}/" 48 | headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36'} 49 | 50 | req = urllib.request.Request(url, headers = headers) 51 | conn = urllib.request.urlopen(req) 52 | 53 | print('fetch conn finish') 54 | 55 | pattern = 'img data-src="\S*"' 56 | img_list = [] 57 | 58 | for match in re.finditer(pattern, str(conn.read())): 59 | img_list.append(match.group()[14:-1]) 60 | 61 | random_img_url = img_list[random.randint(0, len(img_list)+1)] 62 | print('fetch img url finish') 63 | print(random_img_url) 64 | 65 | line_bot_api.reply_message( 66 | event.reply_token, 67 | ImageSendMessage( 68 | original_content_url=random_img_url, 69 | preview_image_url=random_img_url 70 | ) 71 | ) 72 | 73 | # 找不到圖就學說話 74 | except: 75 | line_bot_api.reply_message( 76 | event.reply_token, 77 | TextSendMessage(text=event.message.text) 78 | ) 79 | 80 | if __name__ == "__main__": 81 | app.run() -------------------------------------------------------------------------------- /D14/app_pixabay.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import os 3 | 4 | # 增加了 render_template 5 | from flask import Flask, request, abort, render_template 6 | 7 | from linebot import LineBotApi, WebhookHandler 8 | from linebot.exceptions import InvalidSignatureError 9 | from linebot.models import MessageEvent, TextMessage, TextSendMessage, ImageSendMessage 10 | 11 | import configparser 12 | 13 | import urllib 14 | import re 15 | import random 16 | 17 | app = Flask(__name__) 18 | 19 | # LINE 聊天機器人的基本資料 20 | config = configparser.ConfigParser() 21 | config.read('config.ini') 22 | 23 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 24 | handler = WebhookHandler(config.get('line-bot', 'channel_secret')) 25 | 26 | @app.route("/") 27 | def home(): 28 | return render_template("home_pixijs.html") 29 | 30 | 31 | # 接收 LINE 的資訊 32 | @app.route("/callback", methods=['POST']) 33 | def callback(): 34 | signature = request.headers['X-Line-Signature'] 35 | 36 | body = request.get_data(as_text=True) 37 | app.logger.info("Request body: " + body) 38 | 39 | try: 40 | handler.handle(body, signature) 41 | except InvalidSignatureError: 42 | abort(400) 43 | 44 | return 'OK' 45 | 46 | # 請 pixabay 幫我們找圖 47 | @handler.add(MessageEvent, message=TextMessage) 48 | def pixabay_isch(event): 49 | 50 | if event.source.user_id != "Udeadbeefdeadbeefdeadbeefdeadbeef": 51 | # 先找圖 52 | try: 53 | url = f"https://pixabay.com/images/search/{urllib.parse.urlencode({'q':event.message.text})[2:]}/" 54 | headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36'} 55 | 56 | req = urllib.request.Request(url, headers = headers) 57 | conn = urllib.request.urlopen(req) 58 | 59 | print('fetch page finish') 60 | 61 | pattern = 'img srcset="\S*\s\w*,' 62 | img_list = [] 63 | 64 | for match in re.finditer(pattern, str(conn.read())): 65 | img_list.append(match.group()[12:-3]) 66 | 67 | random_img_url = random.choice(img_list) 68 | print('fetch img url finish') 69 | print(random_img_url) 70 | 71 | line_bot_api.reply_message( 72 | event.reply_token, 73 | ImageSendMessage( 74 | original_content_url=random_img_url, 75 | preview_image_url=random_img_url 76 | ) 77 | ) 78 | # 找不到圖就告訴我 user_id 79 | except: 80 | line_bot_api.reply_message( 81 | event.reply_token, 82 | TextSendMessage(text=str(event.source.user_id)) 83 | ) 84 | 85 | if __name__ == "__main__": 86 | app.run() -------------------------------------------------------------------------------- /D24/custom_models/CallDatabase.py: -------------------------------------------------------------------------------- 1 | import psycopg2 2 | import os 3 | 4 | def line_insert_record(record_list): 5 | DATABASE_URL = os.environ['DATABASE_URL'] 6 | 7 | conn = psycopg2.connect(DATABASE_URL, sslmode='require') 8 | cursor = conn.cursor() 9 | 10 | table_columns = '(alpaca_name, training, duration, date)' 11 | postgres_insert_query = f"""INSERT INTO alpaca_training {table_columns} VALUES (%s,%s,%s,%s)""" 12 | 13 | cursor.executemany(postgres_insert_query, record_list) 14 | conn.commit() 15 | 16 | message = f"恭喜您! {cursor.rowcount} 筆資料成功匯入 alpaca_training 表單!" 17 | print(message) 18 | 19 | cursor.close() 20 | conn.close() 21 | 22 | return message 23 | 24 | def line_select_overall(fetchnumber): 25 | DATABASE_URL = os.environ['DATABASE_URL'] 26 | 27 | conn = psycopg2.connect(DATABASE_URL, sslmode='require') 28 | cursor = conn.cursor() 29 | 30 | postgres_select_query = f"""SELECT * FROM alpaca_training ORDER BY record_no DESC;""" 31 | 32 | cursor.execute(postgres_select_query) 33 | raw = cursor.fetchmany(int(fetchnumber)) 34 | message = [] 35 | 36 | for i in raw: 37 | message.append((i[0], i[1], i[2], str(i[3])[:-3], str(i[4]))) 38 | 39 | cursor.close() 40 | conn.close() 41 | 42 | return message 43 | 44 | def web_select_overall(): 45 | DATABASE_URL = os.environ['DATABASE_URL'] 46 | 47 | conn = psycopg2.connect(DATABASE_URL, sslmode='require') 48 | cursor = conn.cursor() 49 | 50 | postgres_select_query = f"""SELECT * FROM alpaca_training ORDER BY record_no;""" 51 | 52 | cursor.execute(postgres_select_query) 53 | 54 | table = [] 55 | while True: 56 | temp = cursor.fetchmany(10) 57 | 58 | if temp: 59 | table.extend(temp) 60 | else: 61 | break 62 | 63 | cursor.close() 64 | conn.close() 65 | 66 | return table 67 | 68 | # Day24 69 | def web_select_specific(condition): 70 | DATABASE_URL = os.environ['DATABASE_URL'] 71 | 72 | conn = psycopg2.connect(DATABASE_URL, sslmode='require') 73 | cursor = conn.cursor() 74 | 75 | condition_query = [] 76 | 77 | for key, value in condition.items(): 78 | if value: 79 | condition_query.append(f"{key}={value}") 80 | if condition_query: 81 | condition_query = "WHERE " + ' AND '.join(condition_query) 82 | else: 83 | condition_query = '' 84 | 85 | postgres_select_query = f"""SELECT * FROM alpaca_training {condition_query} ORDER BY record_no;""" 86 | print(postgres_select_query) 87 | 88 | cursor.execute(postgres_select_query) 89 | 90 | table = [] 91 | while True: 92 | temp = cursor.fetchmany(10) 93 | 94 | if temp: 95 | table.extend(temp) 96 | else: 97 | break 98 | 99 | cursor.close() 100 | conn.close() 101 | 102 | return table -------------------------------------------------------------------------------- /D25/custom_models/CallDatabase.py: -------------------------------------------------------------------------------- 1 | import psycopg2 2 | import os 3 | 4 | def line_insert_record(record_list): 5 | DATABASE_URL = os.environ['DATABASE_URL'] 6 | 7 | conn = psycopg2.connect(DATABASE_URL, sslmode='require') 8 | cursor = conn.cursor() 9 | 10 | table_columns = '(alpaca_name, training, duration, date)' 11 | postgres_insert_query = f"""INSERT INTO alpaca_training {table_columns} VALUES (%s,%s,%s,%s)""" 12 | 13 | cursor.executemany(postgres_insert_query, record_list) 14 | conn.commit() 15 | 16 | message = f"恭喜您! {cursor.rowcount} 筆資料成功匯入 alpaca_training 表單!" 17 | print(message) 18 | 19 | cursor.close() 20 | conn.close() 21 | 22 | return message 23 | 24 | def line_select_overall(fetchnumber): 25 | DATABASE_URL = os.environ['DATABASE_URL'] 26 | 27 | conn = psycopg2.connect(DATABASE_URL, sslmode='require') 28 | cursor = conn.cursor() 29 | 30 | postgres_select_query = f"""SELECT * FROM alpaca_training ORDER BY record_no DESC;""" 31 | 32 | cursor.execute(postgres_select_query) 33 | raw = cursor.fetchmany(int(fetchnumber)) 34 | message = [] 35 | 36 | for i in raw: 37 | message.append((i[0], i[1], i[2], str(i[3])[:-3], str(i[4]))) 38 | 39 | cursor.close() 40 | conn.close() 41 | 42 | return message 43 | 44 | def web_select_overall(): 45 | DATABASE_URL = os.environ['DATABASE_URL'] 46 | 47 | conn = psycopg2.connect(DATABASE_URL, sslmode='require') 48 | cursor = conn.cursor() 49 | 50 | postgres_select_query = f"""SELECT * FROM alpaca_training ORDER BY record_no;""" 51 | 52 | cursor.execute(postgres_select_query) 53 | 54 | table = [] 55 | while True: 56 | temp = cursor.fetchmany(10) 57 | 58 | if temp: 59 | table.extend(temp) 60 | else: 61 | break 62 | 63 | cursor.close() 64 | conn.close() 65 | 66 | return table 67 | 68 | # Day24 69 | def web_select_specific(condition): 70 | DATABASE_URL = os.environ['DATABASE_URL'] 71 | 72 | conn = psycopg2.connect(DATABASE_URL, sslmode='require') 73 | cursor = conn.cursor() 74 | 75 | condition_query = [] 76 | 77 | for key, value in condition.items(): 78 | if value: 79 | condition_query.append(f"{key}={value}") 80 | if condition_query: 81 | condition_query = "WHERE " + ' AND '.join(condition_query) 82 | else: 83 | condition_query = '' 84 | 85 | postgres_select_query = f"""SELECT * FROM alpaca_training {condition_query} ORDER BY record_no;""" 86 | print(postgres_select_query) 87 | 88 | cursor.execute(postgres_select_query) 89 | 90 | table = [] 91 | while True: 92 | temp = cursor.fetchmany(10) 93 | 94 | if temp: 95 | table.extend(temp) 96 | else: 97 | break 98 | 99 | cursor.close() 100 | conn.close() 101 | 102 | return table -------------------------------------------------------------------------------- /D27/custom_models/CallDatabase.py: -------------------------------------------------------------------------------- 1 | import psycopg2 2 | import os 3 | 4 | def line_insert_record(record_list): 5 | DATABASE_URL = os.environ['DATABASE_URL'] 6 | 7 | conn = psycopg2.connect(DATABASE_URL, sslmode='require') 8 | cursor = conn.cursor() 9 | 10 | table_columns = '(alpaca_name, training, duration, date)' 11 | postgres_insert_query = f"""INSERT INTO alpaca_training {table_columns} VALUES (%s,%s,%s,%s)""" 12 | 13 | cursor.executemany(postgres_insert_query, record_list) 14 | conn.commit() 15 | 16 | message = f"恭喜您! {cursor.rowcount} 筆資料成功匯入 alpaca_training 表單!" 17 | print(message) 18 | 19 | cursor.close() 20 | conn.close() 21 | 22 | return message 23 | 24 | def line_select_overall(fetchnumber): 25 | DATABASE_URL = os.environ['DATABASE_URL'] 26 | 27 | conn = psycopg2.connect(DATABASE_URL, sslmode='require') 28 | cursor = conn.cursor() 29 | 30 | postgres_select_query = f"""SELECT * FROM alpaca_training ORDER BY record_no DESC;""" 31 | 32 | cursor.execute(postgres_select_query) 33 | raw = cursor.fetchmany(int(fetchnumber)) 34 | message = [] 35 | 36 | for i in raw: 37 | message.append((i[0], i[1], i[2], str(i[3])[:-3], str(i[4]))) 38 | 39 | cursor.close() 40 | conn.close() 41 | 42 | return message 43 | 44 | def web_select_overall(): 45 | DATABASE_URL = os.environ['DATABASE_URL'] 46 | 47 | conn = psycopg2.connect(DATABASE_URL, sslmode='require') 48 | cursor = conn.cursor() 49 | 50 | postgres_select_query = f"""SELECT * FROM alpaca_training ORDER BY record_no;""" 51 | 52 | cursor.execute(postgres_select_query) 53 | 54 | table = [] 55 | while True: 56 | temp = cursor.fetchmany(10) 57 | 58 | if temp: 59 | table.extend(temp) 60 | else: 61 | break 62 | 63 | cursor.close() 64 | conn.close() 65 | 66 | return table 67 | 68 | # Day24 69 | def web_select_specific(condition): 70 | DATABASE_URL = os.environ['DATABASE_URL'] 71 | 72 | conn = psycopg2.connect(DATABASE_URL, sslmode='require') 73 | cursor = conn.cursor() 74 | 75 | condition_query = [] 76 | 77 | for key, value in condition.items(): 78 | if value: 79 | condition_query.append(f"{key}={value}") 80 | if condition_query: 81 | condition_query = "WHERE " + ' AND '.join(condition_query) 82 | else: 83 | condition_query = '' 84 | 85 | postgres_select_query = f"""SELECT * FROM alpaca_training {condition_query} ORDER BY record_no;""" 86 | print(postgres_select_query) 87 | 88 | cursor.execute(postgres_select_query) 89 | 90 | table = [] 91 | while True: 92 | temp = cursor.fetchmany(10) 93 | 94 | if temp: 95 | table.extend(temp) 96 | else: 97 | break 98 | 99 | cursor.close() 100 | conn.close() 101 | 102 | return table -------------------------------------------------------------------------------- /D29/custom_models/CallDatabase.py: -------------------------------------------------------------------------------- 1 | import psycopg2 2 | import os 3 | 4 | def line_insert_record(record_list): 5 | DATABASE_URL = os.environ['DATABASE_URL'] 6 | 7 | conn = psycopg2.connect(DATABASE_URL, sslmode='require') 8 | cursor = conn.cursor() 9 | 10 | table_columns = '(alpaca_name, training, duration, date)' 11 | postgres_insert_query = f"""INSERT INTO alpaca_training {table_columns} VALUES (%s,%s,%s,%s)""" 12 | 13 | cursor.executemany(postgres_insert_query, record_list) 14 | conn.commit() 15 | 16 | message = f"恭喜您! {cursor.rowcount} 筆資料成功匯入 alpaca_training 表單!" 17 | print(message) 18 | 19 | cursor.close() 20 | conn.close() 21 | 22 | return message 23 | 24 | def line_select_overall(fetchnumber): 25 | DATABASE_URL = os.environ['DATABASE_URL'] 26 | 27 | conn = psycopg2.connect(DATABASE_URL, sslmode='require') 28 | cursor = conn.cursor() 29 | 30 | postgres_select_query = f"""SELECT * FROM alpaca_training ORDER BY record_no DESC;""" 31 | 32 | cursor.execute(postgres_select_query) 33 | raw = cursor.fetchmany(int(fetchnumber)) 34 | message = [] 35 | 36 | for i in raw: 37 | message.append((i[0], i[1], i[2], str(i[3])[:-3], str(i[4]))) 38 | 39 | cursor.close() 40 | conn.close() 41 | 42 | return message 43 | 44 | def web_select_overall(): 45 | DATABASE_URL = os.environ['DATABASE_URL'] 46 | 47 | conn = psycopg2.connect(DATABASE_URL, sslmode='require') 48 | cursor = conn.cursor() 49 | 50 | postgres_select_query = f"""SELECT * FROM alpaca_training ORDER BY record_no;""" 51 | 52 | cursor.execute(postgres_select_query) 53 | 54 | table = [] 55 | while True: 56 | temp = cursor.fetchmany(10) 57 | 58 | if temp: 59 | table.extend(temp) 60 | else: 61 | break 62 | 63 | cursor.close() 64 | conn.close() 65 | 66 | return table 67 | 68 | # Day24 69 | def web_select_specific(condition): 70 | DATABASE_URL = os.environ['DATABASE_URL'] 71 | 72 | conn = psycopg2.connect(DATABASE_URL, sslmode='require') 73 | cursor = conn.cursor() 74 | 75 | condition_query = [] 76 | 77 | for key, value in condition.items(): 78 | if value: 79 | condition_query.append(f"{key}={value}") 80 | if condition_query: 81 | condition_query = "WHERE " + ' AND '.join(condition_query) 82 | else: 83 | condition_query = '' 84 | 85 | postgres_select_query = f"""SELECT * FROM alpaca_training {condition_query} ORDER BY record_no;""" 86 | print(postgres_select_query) 87 | 88 | cursor.execute(postgres_select_query) 89 | 90 | table = [] 91 | while True: 92 | temp = cursor.fetchmany(10) 93 | 94 | if temp: 95 | table.extend(temp) 96 | else: 97 | break 98 | 99 | cursor.close() 100 | conn.close() 101 | 102 | return table -------------------------------------------------------------------------------- /D17/templates/home_pixijs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 48 | 49 | 50 |
51 |

PHOEBE WEATHERFIELD
PIXI.JS


52 |

credit: "https://codepen.io/ricardoolivaalonso/pen/rNBLdoa"


53 | click here 54 |
55 | 56 | 57 | 106 | 107 | -------------------------------------------------------------------------------- /D19/templates/home_pixijs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 48 | 49 | 50 |
51 |

PHOEBE WEATHERFIELD
PIXI.JS


52 |

credit: "https://codepen.io/ricardoolivaalonso/pen/rNBLdoa"


53 |
click here 54 |
55 | 56 | 57 | 106 | 107 | -------------------------------------------------------------------------------- /D24/app_day_24.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import os 3 | from flask import Flask, request, abort, render_template 4 | from linebot import LineBotApi, WebhookHandler 5 | from linebot.exceptions import InvalidSignatureError 6 | from linebot.models import MessageEvent, PostbackEvent, TextMessage, TextSendMessage 7 | 8 | import configparser 9 | 10 | from custom_models import utils, PhoebeTalks, PhoebeFlex 11 | 12 | app = Flask(__name__) 13 | 14 | # LINE 聊天機器人的基本資料 15 | config = configparser.ConfigParser() 16 | config.read('config.ini') 17 | 18 | line_bot_api = LineBotApi(config.get('line-bot', 'channel_access_token')) 19 | handler = WebhookHandler(config.get('line-bot', 'channel_secret')) 20 | 21 | @app.route("/") 22 | def home(): 23 | return render_template("home.html") 24 | 25 | @app.route("/from_start") 26 | def from_start(): 27 | return render_template("from_start.html") 28 | 29 | @app.route("/show_records") 30 | def show_records(): 31 | python_records = CallDatabase.web_select_overall() 32 | return render_template("show_records.html", html_records=python_records) 33 | 34 | # Day24: 選擇訓練紀錄 35 | @app.route("/select_records", methods=['GET', 'POST']) 36 | def select_records(): 37 | if request.method == 'POST': 38 | print(request.form) 39 | python_records = CallDatabase.web_select_specific(request.form) 40 | return render_template("show_records.html", html_records=python_records) 41 | else: 42 | return render_template("select_records.html") 43 | 44 | # Day24: 舒適地選擇訓練紀錄 45 | @app.route("/select_records_comfortable", methods=['GET', 'POST']) 46 | def select_records_comfortable(): 47 | if request.method == 'POST': 48 | print(request.form) 49 | python_records = CallDatabase.web_select_specific(request.form) 50 | return render_template("show_records.html", html_records=python_records) 51 | else: 52 | table = CallDatabase.web_select_overall() 53 | uniques = utils.get_unique(table) 54 | return render_template("select_records_comfortable.html", uniques=uniques) 55 | 56 | 57 | # 接收 LINE 的資訊 58 | @app.route("/callback", methods=['POST']) 59 | def callback(): 60 | signature = request.headers['X-Line-Signature'] 61 | 62 | body = request.get_data(as_text=True) 63 | app.logger.info("Request body: " + body) 64 | 65 | try: 66 | handler.handle(body, signature) 67 | except InvalidSignatureError: 68 | abort(400) 69 | 70 | return 'OK' 71 | 72 | # 處理 MessageEvent 中的 TextMessage 73 | @handler.add(MessageEvent, message=TextMessage) 74 | def reply_text_message(event): 75 | 76 | if event.source.user_id != "Udeadbeefdeadbeefdeadbeefdeadbeef": 77 | 78 | reply = False 79 | 80 | # 將資料存入表格中 81 | if not reply: 82 | reply = PhoebeTalks.insert_record(event) 83 | 84 | # 發送 FlexMessage 85 | if not reply: 86 | reply = PhoebeFlex.img_search_flex(event) 87 | 88 | # 幫忙上網找圖 89 | if not reply: 90 | reply = PhoebeTalks.img_search(event) 91 | 92 | # 裝飾過的回音機器人 93 | if not reply: 94 | reply = PhoebeTalks.pretty_echo(event) 95 | 96 | 97 | if __name__ == "__main__": 98 | app.run() -------------------------------------------------------------------------------- /D22/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | {% block title %}{% endblock %} 23 | 24 | 25 | 26 | 27 | 66 | 67 |
68 | {% block main %}{% endblock %} 69 |
70 | 71 | 86 | 87 | 88 | 89 | 90 | 91 | {% block script %}{% endblock %} 92 | 93 | -------------------------------------------------------------------------------- /D23/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | {% block title %}{% endblock %} 23 | 24 | 25 | 26 | 27 | 67 | 68 |
69 | {% block main %}{% endblock %} 70 |
71 | 72 | 87 | 88 | 89 | 90 | 91 | 92 | {% block script %}{% endblock %} 93 | 94 | -------------------------------------------------------------------------------- /D19/custom_models/utils.py: -------------------------------------------------------------------------------- 1 | import urllib 2 | import re 3 | import random 4 | import datetime 5 | 6 | # 請 pixabay 幫我們找圖 7 | def get_img_url(img_source, target): 8 | 9 | img_source_dict = { 10 | 'google': [f"https://www.google.com/search?{urllib.parse.urlencode(dict([['tbm', 'isch'], ['q', target]]))}/", 11 | 'img data-src="\S*"', 12 | 14, 13 | -1], 14 | 'pixabay': [f"https://pixabay.com/images/search/{urllib.parse.urlencode(dict([['q', target]]))[2:]}/", 15 | 'img srcset="\S*\s\w*,', 16 | 12, 17 | -4], 18 | 'unsplash': [f"https://unsplash.com/s/photos/{urllib.parse.urlencode(dict([['q', target]]))[2:]}/", 19 | 'srcSet="\S* ', 20 | 8, 21 | -1]} 22 | 23 | url = img_source_dict[img_source][0] 24 | headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36'} 25 | 26 | req = urllib.request.Request(url, headers = headers) 27 | conn = urllib.request.urlopen(req) 28 | 29 | print('fetch page finish') 30 | 31 | img_list = [] 32 | 33 | for match in re.finditer(img_source_dict[img_source][1], str(conn.read())): 34 | img_list.append(match.group()[img_source_dict[img_source][2]:img_source_dict[img_source][3]]) 35 | 36 | random_img_url = random.choice(img_list) 37 | print('fetch img url finish') 38 | print(random_img_url) 39 | 40 | return random_img_url 41 | 42 | # 準備查詢的資料 43 | def prepare_record(text): 44 | text_list = text.split('\n') 45 | 46 | month = text_list[0].split(' ')[0].split('/')[0] 47 | day = text_list[0].split(' ')[0].split('/')[1] 48 | d = datetime.date(datetime.date.today().year, int(month), int(day)) 49 | 50 | record_list = [] 51 | 52 | time_format = '%H:%M' 53 | 54 | for i in text_list[1:]: 55 | temp_list = i.split(' ') 56 | 57 | temp_name = temp_list[0] 58 | temp_training = temp_list[1] 59 | 60 | temp_start = datetime.datetime.strptime(temp_list[2].split('-')[0], time_format) 61 | temp_end = datetime.datetime.strptime(temp_list[2].split('-')[1], time_format) 62 | temp_duration = temp_end - temp_start 63 | 64 | record = (temp_name, temp_training, temp_duration, d) 65 | record_list.append(record) 66 | 67 | return record_list 68 | 69 | 70 | # 上 google 找翻譯 71 | def get_translate(text): 72 | 73 | translate = f'{text} 英文' 74 | 75 | url = f"https://www.google.com/search?{urllib.parse.urlencode(dict([['oq', translate], ['q', translate]]))}/" 76 | headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36'} 77 | 78 | req = urllib.request.Request(url, headers = headers) 79 | conn = urllib.request.urlopen(req) 80 | 81 | data = conn.read() 82 | 83 | pattern = '\S*' 84 | match = re.search(pattern, str(data)) 85 | 86 | return match.group()[19:-7] 87 | 88 | # 根據輸入的參數製造 FlexMessage 89 | def prepare_img_search_flex(text, translate, random_img_url): 90 | contents ={"type": "bubble", 91 | "header": { 92 | "type": "box", 93 | "layout": "horizontal", 94 | "contents": [ 95 | {"type": "text", "text": text, "size": "xl", "weight": "bold"}, 96 | {"type": "text", "text": translate, "size": "lg", "color": "#888888", "align": "end", "gravity": "bottom"} 97 | ] 98 | }, 99 | "hero": { 100 | "type": "image", 101 | "url": random_img_url, 102 | "size": "full", 103 | "aspect_ratio": "20:13", 104 | "aspect_mode": "cover" 105 | }, 106 | "body": { 107 | "type": "box", 108 | "layout": "vertical", 109 | "spacing": "md", 110 | "contents": [ 111 | {"type": "text", "text": "圖片的出處在這裡", "size": "md", "weight": "bold"} 112 | ] 113 | }, 114 | "footer": { 115 | "type": "box", 116 | "layout": "vertical", 117 | "contents": [ 118 | {"type": "spacer", "size": "md"}, 119 | {"type": "button", "style": "primary", "color": "#1DB446", 120 | "action": {"type": "uri", "label": "GO", "uri": random_img_url}} 121 | ] 122 | } 123 | } 124 | return contents -------------------------------------------------------------------------------- /D20/custom_models/utils.py: -------------------------------------------------------------------------------- 1 | import urllib 2 | import re 3 | import random 4 | import datetime 5 | 6 | # 請 pixabay 幫我們找圖 7 | def get_img_url(img_source, target): 8 | 9 | img_source_dict = { 10 | 'google': [f"https://www.google.com/search?{urllib.parse.urlencode(dict([['tbm', 'isch'], ['q', target]]))}/", 11 | 'img data-src="\S*"', 12 | 14, 13 | -1], 14 | 'pixabay': [f"https://pixabay.com/images/search/{urllib.parse.urlencode(dict([['q', target]]))[2:]}/", 15 | 'img srcset="\S*\s\w*,', 16 | 12, 17 | -4], 18 | 'unsplash': [f"https://unsplash.com/s/photos/{urllib.parse.urlencode(dict([['q', target]]))[2:]}/", 19 | 'srcSet="\S* ', 20 | 8, 21 | -1]} 22 | 23 | url = img_source_dict[img_source][0] 24 | headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36'} 25 | 26 | req = urllib.request.Request(url, headers = headers) 27 | conn = urllib.request.urlopen(req) 28 | 29 | print('fetch page finish') 30 | 31 | img_list = [] 32 | 33 | for match in re.finditer(img_source_dict[img_source][1], str(conn.read())): 34 | img_list.append(match.group()[img_source_dict[img_source][2]:img_source_dict[img_source][3]]) 35 | 36 | random_img_url = random.choice(img_list) 37 | print('fetch img url finish') 38 | print(random_img_url) 39 | 40 | return random_img_url 41 | 42 | # 準備查詢的資料 43 | def prepare_record(text): 44 | text_list = text.split('\n') 45 | 46 | month = text_list[0].split(' ')[0].split('/')[0] 47 | day = text_list[0].split(' ')[0].split('/')[1] 48 | d = datetime.date(datetime.date.today().year, int(month), int(day)) 49 | 50 | record_list = [] 51 | 52 | time_format = '%H:%M' 53 | 54 | for i in text_list[1:]: 55 | temp_list = i.split(' ') 56 | 57 | temp_name = temp_list[0] 58 | temp_training = temp_list[1] 59 | 60 | temp_start = datetime.datetime.strptime(temp_list[2].split('-')[0], time_format) 61 | temp_end = datetime.datetime.strptime(temp_list[2].split('-')[1], time_format) 62 | temp_duration = temp_end - temp_start 63 | 64 | record = (temp_name, temp_training, temp_duration, d) 65 | record_list.append(record) 66 | 67 | return record_list 68 | 69 | 70 | # 上 google 找翻譯 71 | def get_translate(text): 72 | 73 | translate = f'{text} 英文' 74 | 75 | url = f"https://www.google.com/search?{urllib.parse.urlencode(dict([['oq', translate], ['q', translate]]))}/" 76 | headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36'} 77 | 78 | req = urllib.request.Request(url, headers = headers) 79 | conn = urllib.request.urlopen(req) 80 | 81 | data = conn.read() 82 | 83 | pattern = '\S*' 84 | match = re.search(pattern, str(data)) 85 | 86 | return match.group()[19:-7] 87 | 88 | # 根據輸入的參數製造 FlexMessage 89 | def prepare_img_search_flex(text, translate, random_img_url): 90 | contents ={"type": "bubble", 91 | "header": { 92 | "type": "box", 93 | "layout": "horizontal", 94 | "contents": [ 95 | {"type": "text", "text": text, "size": "xl", "weight": "bold"}, 96 | {"type": "text", "text": translate, "size": "lg", "color": "#888888", "align": "end", "gravity": "bottom"} 97 | ] 98 | }, 99 | "hero": { 100 | "type": "image", 101 | "url": random_img_url, 102 | "size": "full", 103 | "aspect_ratio": "20:13", 104 | "aspect_mode": "cover" 105 | }, 106 | "body": { 107 | "type": "box", 108 | "layout": "vertical", 109 | "spacing": "md", 110 | "contents": [ 111 | {"type": "text", "text": "圖片的出處在這裡", "size": "md", "weight": "bold"} 112 | ] 113 | }, 114 | "footer": { 115 | "type": "box", 116 | "layout": "vertical", 117 | "contents": [ 118 | {"type": "spacer", "size": "md"}, 119 | {"type": "button", "style": "primary", "color": "#1DB446", 120 | "action": {"type": "uri", "label": "GO", "uri": random_img_url}} 121 | ] 122 | } 123 | } 124 | return contents -------------------------------------------------------------------------------- /D22/custom_models/utils.py: -------------------------------------------------------------------------------- 1 | import urllib 2 | import re 3 | import random 4 | import datetime 5 | 6 | # 請 pixabay 幫我們找圖 7 | def get_img_url(img_source, target): 8 | 9 | img_source_dict = { 10 | 'google': [f"https://www.google.com/search?{urllib.parse.urlencode(dict([['tbm', 'isch'], ['q', target]]))}/", 11 | 'img data-src="\S*"', 12 | 14, 13 | -1], 14 | 'pixabay': [f"https://pixabay.com/images/search/{urllib.parse.urlencode(dict([['q', target]]))[2:]}/", 15 | 'img srcset="\S*\s\w*,', 16 | 12, 17 | -4], 18 | 'unsplash': [f"https://unsplash.com/s/photos/{urllib.parse.urlencode(dict([['q', target]]))[2:]}/", 19 | 'srcSet="\S* ', 20 | 8, 21 | -1]} 22 | 23 | url = img_source_dict[img_source][0] 24 | headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36'} 25 | 26 | req = urllib.request.Request(url, headers = headers) 27 | conn = urllib.request.urlopen(req) 28 | 29 | print('fetch page finish') 30 | 31 | img_list = [] 32 | 33 | for match in re.finditer(img_source_dict[img_source][1], str(conn.read())): 34 | img_list.append(match.group()[img_source_dict[img_source][2]:img_source_dict[img_source][3]]) 35 | 36 | random_img_url = random.choice(img_list) 37 | print('fetch img url finish') 38 | print(random_img_url) 39 | 40 | return random_img_url 41 | 42 | # 準備查詢的資料 43 | def prepare_record(text): 44 | text_list = text.split('\n') 45 | 46 | month = text_list[0].split(' ')[0].split('/')[0] 47 | day = text_list[0].split(' ')[0].split('/')[1] 48 | d = datetime.date(datetime.date.today().year, int(month), int(day)) 49 | 50 | record_list = [] 51 | 52 | time_format = '%H:%M' 53 | 54 | for i in text_list[1:]: 55 | temp_list = i.split(' ') 56 | 57 | temp_name = temp_list[0] 58 | temp_training = temp_list[1] 59 | 60 | temp_start = datetime.datetime.strptime(temp_list[2].split('-')[0], time_format) 61 | temp_end = datetime.datetime.strptime(temp_list[2].split('-')[1], time_format) 62 | temp_duration = temp_end - temp_start 63 | 64 | record = (temp_name, temp_training, temp_duration, d) 65 | record_list.append(record) 66 | 67 | return record_list 68 | 69 | 70 | # 上 google 找翻譯 71 | def get_translate(text): 72 | 73 | translate = f'{text} 英文' 74 | 75 | url = f"https://www.google.com/search?{urllib.parse.urlencode(dict([['oq', translate], ['q', translate]]))}/" 76 | headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36'} 77 | 78 | req = urllib.request.Request(url, headers = headers) 79 | conn = urllib.request.urlopen(req) 80 | 81 | data = conn.read() 82 | 83 | pattern = '\S*' 84 | match = re.search(pattern, str(data)) 85 | 86 | return match.group()[19:-7] 87 | 88 | # 根據輸入的參數製造 FlexMessage 89 | def prepare_img_search_flex(text, translate, random_img_url): 90 | contents ={"type": "bubble", 91 | "header": { 92 | "type": "box", 93 | "layout": "horizontal", 94 | "contents": [ 95 | {"type": "text", "text": text, "size": "xl", "weight": "bold"}, 96 | {"type": "text", "text": translate, "size": "lg", "color": "#888888", "align": "end", "gravity": "bottom"} 97 | ] 98 | }, 99 | "hero": { 100 | "type": "image", 101 | "url": random_img_url, 102 | "size": "full", 103 | "aspect_ratio": "20:13", 104 | "aspect_mode": "cover" 105 | }, 106 | "body": { 107 | "type": "box", 108 | "layout": "vertical", 109 | "spacing": "md", 110 | "contents": [ 111 | {"type": "text", "text": "圖片的出處在這裡", "size": "md", "weight": "bold"} 112 | ] 113 | }, 114 | "footer": { 115 | "type": "box", 116 | "layout": "vertical", 117 | "contents": [ 118 | {"type": "spacer", "size": "md"}, 119 | {"type": "button", "style": "primary", "color": "#1DB446", 120 | "action": {"type": "uri", "label": "GO", "uri": random_img_url}} 121 | ] 122 | } 123 | } 124 | return contents -------------------------------------------------------------------------------- /D23/custom_models/utils.py: -------------------------------------------------------------------------------- 1 | import urllib 2 | import re 3 | import random 4 | import datetime 5 | 6 | # 請 pixabay 幫我們找圖 7 | def get_img_url(img_source, target): 8 | 9 | img_source_dict = { 10 | 'google': [f"https://www.google.com/search?{urllib.parse.urlencode(dict([['tbm', 'isch'], ['q', target]]))}/", 11 | 'img data-src="\S*"', 12 | 14, 13 | -1], 14 | 'pixabay': [f"https://pixabay.com/images/search/{urllib.parse.urlencode(dict([['q', target]]))[2:]}/", 15 | 'img srcset="\S*\s\w*,', 16 | 12, 17 | -4], 18 | 'unsplash': [f"https://unsplash.com/s/photos/{urllib.parse.urlencode(dict([['q', target]]))[2:]}/", 19 | 'srcSet="\S* ', 20 | 8, 21 | -1]} 22 | 23 | url = img_source_dict[img_source][0] 24 | headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36'} 25 | 26 | req = urllib.request.Request(url, headers = headers) 27 | conn = urllib.request.urlopen(req) 28 | 29 | print('fetch page finish') 30 | 31 | img_list = [] 32 | 33 | for match in re.finditer(img_source_dict[img_source][1], str(conn.read())): 34 | img_list.append(match.group()[img_source_dict[img_source][2]:img_source_dict[img_source][3]]) 35 | 36 | random_img_url = random.choice(img_list) 37 | print('fetch img url finish') 38 | print(random_img_url) 39 | 40 | return random_img_url 41 | 42 | # 準備查詢的資料 43 | def prepare_record(text): 44 | text_list = text.split('\n') 45 | 46 | month = text_list[0].split(' ')[0].split('/')[0] 47 | day = text_list[0].split(' ')[0].split('/')[1] 48 | d = datetime.date(datetime.date.today().year, int(month), int(day)) 49 | 50 | record_list = [] 51 | 52 | time_format = '%H:%M' 53 | 54 | for i in text_list[1:]: 55 | temp_list = i.split(' ') 56 | 57 | temp_name = temp_list[0] 58 | temp_training = temp_list[1] 59 | 60 | temp_start = datetime.datetime.strptime(temp_list[2].split('-')[0], time_format) 61 | temp_end = datetime.datetime.strptime(temp_list[2].split('-')[1], time_format) 62 | temp_duration = temp_end - temp_start 63 | 64 | record = (temp_name, temp_training, temp_duration, d) 65 | record_list.append(record) 66 | 67 | return record_list 68 | 69 | 70 | # 上 google 找翻譯 71 | def get_translate(text): 72 | 73 | translate = f'{text} 英文' 74 | 75 | url = f"https://www.google.com/search?{urllib.parse.urlencode(dict([['oq', translate], ['q', translate]]))}/" 76 | headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36'} 77 | 78 | req = urllib.request.Request(url, headers = headers) 79 | conn = urllib.request.urlopen(req) 80 | 81 | data = conn.read() 82 | 83 | pattern = '\S*' 84 | match = re.search(pattern, str(data)) 85 | 86 | return match.group()[19:-7] 87 | 88 | # 根據輸入的參數製造 FlexMessage 89 | def prepare_img_search_flex(text, translate, random_img_url): 90 | contents ={"type": "bubble", 91 | "header": { 92 | "type": "box", 93 | "layout": "horizontal", 94 | "contents": [ 95 | {"type": "text", "text": text, "size": "xl", "weight": "bold"}, 96 | {"type": "text", "text": translate, "size": "lg", "color": "#888888", "align": "end", "gravity": "bottom"} 97 | ] 98 | }, 99 | "hero": { 100 | "type": "image", 101 | "url": random_img_url, 102 | "size": "full", 103 | "aspect_ratio": "20:13", 104 | "aspect_mode": "cover" 105 | }, 106 | "body": { 107 | "type": "box", 108 | "layout": "vertical", 109 | "spacing": "md", 110 | "contents": [ 111 | {"type": "text", "text": "圖片的出處在這裡", "size": "md", "weight": "bold"} 112 | ] 113 | }, 114 | "footer": { 115 | "type": "box", 116 | "layout": "vertical", 117 | "contents": [ 118 | {"type": "spacer", "size": "md"}, 119 | {"type": "button", "style": "primary", "color": "#1DB446", 120 | "action": {"type": "uri", "label": "GO", "uri": random_img_url}} 121 | ] 122 | } 123 | } 124 | return contents -------------------------------------------------------------------------------- /D24/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | {% block title %}{% endblock %} 25 | 26 | 27 | 28 | 29 | 73 | 74 |
75 | {% block main %}{% endblock %} 76 |
77 | 78 | 93 | 94 | 95 | 96 | 97 | 98 | {% block script %}{% endblock %} 99 | 100 | -------------------------------------------------------------------------------- /D24/custom_models/utils.py: -------------------------------------------------------------------------------- 1 | import urllib 2 | import re 3 | import random 4 | import datetime 5 | 6 | # 請 pixabay 幫我們找圖 7 | def get_img_url(img_source, target): 8 | 9 | img_source_dict = { 10 | 'google': [f"https://www.google.com/search?{urllib.parse.urlencode(dict([['tbm', 'isch'], ['q', target]]))}/", 11 | 'img data-src="\S*"', 12 | 14, 13 | -1], 14 | 'pixabay': [f"https://pixabay.com/images/search/{urllib.parse.urlencode(dict([['q', target]]))[2:]}/", 15 | 'img srcset="\S*\s\w*,', 16 | 12, 17 | -4], 18 | 'unsplash': [f"https://unsplash.com/s/photos/{urllib.parse.urlencode(dict([['q', target]]))[2:]}/", 19 | 'srcSet="\S* ', 20 | 8, 21 | -1]} 22 | 23 | url = img_source_dict[img_source][0] 24 | headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36'} 25 | 26 | req = urllib.request.Request(url, headers = headers) 27 | conn = urllib.request.urlopen(req) 28 | 29 | print('fetch page finish') 30 | 31 | img_list = [] 32 | 33 | for match in re.finditer(img_source_dict[img_source][1], str(conn.read())): 34 | img_list.append(match.group()[img_source_dict[img_source][2]:img_source_dict[img_source][3]]) 35 | 36 | random_img_url = random.choice(img_list) 37 | print('fetch img url finish') 38 | print(random_img_url) 39 | 40 | return random_img_url 41 | 42 | # 準備查詢的資料 43 | def prepare_record(text): 44 | text_list = text.split('\n') 45 | 46 | month = text_list[0].split(' ')[0].split('/')[0] 47 | day = text_list[0].split(' ')[0].split('/')[1] 48 | d = datetime.date(datetime.date.today().year, int(month), int(day)) 49 | 50 | record_list = [] 51 | 52 | time_format = '%H:%M' 53 | 54 | for i in text_list[1:]: 55 | temp_list = i.split(' ') 56 | 57 | temp_name = temp_list[0] 58 | temp_training = temp_list[1] 59 | 60 | temp_start = datetime.datetime.strptime(temp_list[2].split('-')[0], time_format) 61 | temp_end = datetime.datetime.strptime(temp_list[2].split('-')[1], time_format) 62 | temp_duration = temp_end - temp_start 63 | 64 | record = (temp_name, temp_training, temp_duration, d) 65 | record_list.append(record) 66 | 67 | return record_list 68 | 69 | # 上 google 找翻譯 70 | def get_translate(text): 71 | 72 | translate = f'{text} 英文' 73 | 74 | url = f"https://www.google.com/search?{urllib.parse.urlencode(dict([['oq', translate], ['q', translate]]))}/" 75 | headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36'} 76 | 77 | req = urllib.request.Request(url, headers = headers) 78 | conn = urllib.request.urlopen(req) 79 | 80 | data = conn.read() 81 | 82 | pattern = '\S*' 83 | match = re.search(pattern, str(data)) 84 | 85 | return match.group()[19:-7] 86 | 87 | # 根據輸入的參數製造 FlexMessage 88 | def prepare_img_search_flex(text, translate, random_img_url): 89 | contents ={"type": "bubble", 90 | "header": { 91 | "type": "box", 92 | "layout": "horizontal", 93 | "contents": [ 94 | {"type": "text", "text": text, "size": "xl", "weight": "bold"}, 95 | {"type": "text", "text": translate, "size": "lg", "color": "#888888", "align": "end", "gravity": "bottom"} 96 | ] 97 | }, 98 | "hero": { 99 | "type": "image", 100 | "url": random_img_url, 101 | "size": "full", 102 | "aspect_ratio": "20:13", 103 | "aspect_mode": "cover" 104 | }, 105 | "body": { 106 | "type": "box", 107 | "layout": "vertical", 108 | "spacing": "md", 109 | "contents": [ 110 | {"type": "text", "text": "圖片的出處在這裡", "size": "md", "weight": "bold"} 111 | ] 112 | }, 113 | "footer": { 114 | "type": "box", 115 | "layout": "vertical", 116 | "contents": [ 117 | {"type": "spacer", "size": "md"}, 118 | {"type": "button", "style": "primary", "color": "#1DB446", 119 | "action": {"type": "uri", "label": "GO", "uri": random_img_url}} 120 | ] 121 | } 122 | } 123 | return contents 124 | 125 | # Day24: 列出每個欄位中的所有選項 126 | def get_unique(table): 127 | unique_alpaca_name = {i[1] for i in table} 128 | unique_training = {i[2] for i in table} 129 | unique_date = {i[4] for i in table} 130 | 131 | return sorted(unique_alpaca_name), sorted(unique_training), sorted(unique_date) -------------------------------------------------------------------------------- /D25/custom_models/utils.py: -------------------------------------------------------------------------------- 1 | import urllib 2 | import re 3 | import random 4 | import datetime 5 | 6 | # 請 pixabay 幫我們找圖 7 | def get_img_url(img_source, target): 8 | 9 | img_source_dict = { 10 | 'google': [f"https://www.google.com/search?{urllib.parse.urlencode(dict([['tbm', 'isch'], ['q', target]]))}/", 11 | 'img data-src="\S*"', 12 | 14, 13 | -1], 14 | 'pixabay': [f"https://pixabay.com/images/search/{urllib.parse.urlencode(dict([['q', target]]))[2:]}/", 15 | 'img srcset="\S*\s\w*,', 16 | 12, 17 | -4], 18 | 'unsplash': [f"https://unsplash.com/s/photos/{urllib.parse.urlencode(dict([['q', target]]))[2:]}/", 19 | 'srcSet="\S* ', 20 | 8, 21 | -1]} 22 | 23 | url = img_source_dict[img_source][0] 24 | headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36'} 25 | 26 | req = urllib.request.Request(url, headers = headers) 27 | conn = urllib.request.urlopen(req) 28 | 29 | print('fetch page finish') 30 | 31 | img_list = [] 32 | 33 | for match in re.finditer(img_source_dict[img_source][1], str(conn.read())): 34 | img_list.append(match.group()[img_source_dict[img_source][2]:img_source_dict[img_source][3]]) 35 | 36 | random_img_url = random.choice(img_list) 37 | print('fetch img url finish') 38 | print(random_img_url) 39 | 40 | return random_img_url 41 | 42 | # 準備查詢的資料 43 | def prepare_record(text): 44 | text_list = text.split('\n') 45 | 46 | month = text_list[0].split(' ')[0].split('/')[0] 47 | day = text_list[0].split(' ')[0].split('/')[1] 48 | d = datetime.date(datetime.date.today().year, int(month), int(day)) 49 | 50 | record_list = [] 51 | 52 | time_format = '%H:%M' 53 | 54 | for i in text_list[1:]: 55 | temp_list = i.split(' ') 56 | 57 | temp_name = temp_list[0] 58 | temp_training = temp_list[1] 59 | 60 | temp_start = datetime.datetime.strptime(temp_list[2].split('-')[0], time_format) 61 | temp_end = datetime.datetime.strptime(temp_list[2].split('-')[1], time_format) 62 | temp_duration = temp_end - temp_start 63 | 64 | record = (temp_name, temp_training, temp_duration, d) 65 | record_list.append(record) 66 | 67 | return record_list 68 | 69 | # 上 google 找翻譯 70 | def get_translate(text): 71 | 72 | translate = f'{text} 英文' 73 | 74 | url = f"https://www.google.com/search?{urllib.parse.urlencode(dict([['oq', translate], ['q', translate]]))}/" 75 | headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36'} 76 | 77 | req = urllib.request.Request(url, headers = headers) 78 | conn = urllib.request.urlopen(req) 79 | 80 | data = conn.read() 81 | 82 | pattern = '\S*' 83 | match = re.search(pattern, str(data)) 84 | 85 | return match.group()[19:-7] 86 | 87 | # 根據輸入的參數製造 FlexMessage 88 | def prepare_img_search_flex(text, translate, random_img_url): 89 | contents ={"type": "bubble", 90 | "header": { 91 | "type": "box", 92 | "layout": "horizontal", 93 | "contents": [ 94 | {"type": "text", "text": text, "size": "xl", "weight": "bold"}, 95 | {"type": "text", "text": translate, "size": "lg", "color": "#888888", "align": "end", "gravity": "bottom"} 96 | ] 97 | }, 98 | "hero": { 99 | "type": "image", 100 | "url": random_img_url, 101 | "size": "full", 102 | "aspect_ratio": "20:13", 103 | "aspect_mode": "cover" 104 | }, 105 | "body": { 106 | "type": "box", 107 | "layout": "vertical", 108 | "spacing": "md", 109 | "contents": [ 110 | {"type": "text", "text": "圖片的出處在這裡", "size": "md", "weight": "bold"} 111 | ] 112 | }, 113 | "footer": { 114 | "type": "box", 115 | "layout": "vertical", 116 | "contents": [ 117 | {"type": "spacer", "size": "md"}, 118 | {"type": "button", "style": "primary", "color": "#1DB446", 119 | "action": {"type": "uri", "label": "GO", "uri": random_img_url}} 120 | ] 121 | } 122 | } 123 | return contents 124 | 125 | # Day24: 列出每個欄位中的所有選項 126 | def get_unique(table): 127 | unique_alpaca_name = {i[1] for i in table} 128 | unique_training = {i[2] for i in table} 129 | unique_date = {i[4] for i in table} 130 | 131 | return sorted(unique_alpaca_name), sorted(unique_training), sorted(unique_date) -------------------------------------------------------------------------------- /D15 - Heroku Postgres.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# 標準 psycopg2 流程" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": null, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "import os\n", 17 | "import psycopg2\n", 18 | "\n", 19 | "DATABASE_URL = os.popen('heroku config:get DATABASE_URL -a 你-APP-的名字').read()[:-1]\n", 20 | "\n", 21 | "conn = psycopg2.connect(DATABASE_URL, sslmode='require')\n", 22 | "cursor = conn.cursor()\n", 23 | "\n", 24 | "SQL_order = '''我們輸入 SQL 指令的位置'''\n", 25 | "\n", 26 | "cursor.execute(SQL_order)\n", 27 | "conn.commit()\n", 28 | "\n", 29 | "cursor.close()\n", 30 | "conn.close()" 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "# 標準 psycopg2 流程:新增表格(table)" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": null, 43 | "metadata": {}, 44 | "outputs": [], 45 | "source": [ 46 | "import os\n", 47 | "import psycopg2\n", 48 | "\n", 49 | "DATABASE_URL = os.popen('heroku config:get DATABASE_URL -a 你-APP-的名字').read()[:-1]\n", 50 | "\n", 51 | "conn = psycopg2.connect(DATABASE_URL, sslmode='require')\n", 52 | "cursor = conn.cursor()\n", 53 | "\n", 54 | "create_table_query = '''CREATE TABLE account(\n", 55 | " user_id serial PRIMARY KEY,\n", 56 | " username VARCHAR (50) UNIQUE NOT NULL,\n", 57 | " password VARCHAR (50) NOT NULL,\n", 58 | " email VARCHAR (355) UNIQUE NOT NULL,\n", 59 | " created_on TIMESTAMP NOT NULL,\n", 60 | " last_login TIMESTAMP\n", 61 | ");'''\n", 62 | " \n", 63 | "cursor.execute(create_table_query)\n", 64 | "conn.commit()\n", 65 | "\n", 66 | "cursor.close()\n", 67 | "conn.close()" 68 | ] 69 | }, 70 | { 71 | "cell_type": "code", 72 | "execution_count": null, 73 | "metadata": {}, 74 | "outputs": [], 75 | "source": [ 76 | "import os\n", 77 | "import psycopg2\n", 78 | "\n", 79 | "DATABASE_URL = os.popen('heroku config:get DATABASE_URL -a 你-APP-的名字').read()[:-1]\n", 80 | "\n", 81 | "conn = psycopg2.connect(DATABASE_URL, sslmode='require')\n", 82 | "cursor = conn.cursor()\n", 83 | "\n", 84 | "create_table_query = '''CREATE TABLE alpaca_training(\n", 85 | " record_no serial PRIMARY KEY,\n", 86 | " alpaca_name VARCHAR (50) NOT NULL,\n", 87 | " training VARCHAR (50) NOT NULL,\n", 88 | " duration INTERVAL NOT NULL,\n", 89 | " date DATE NOT NULL\n", 90 | ");'''\n", 91 | " \n", 92 | "cursor.execute(create_table_query)\n", 93 | "conn.commit()\n", 94 | "\n", 95 | "cursor.close()\n", 96 | "conn.close()" 97 | ] 98 | }, 99 | { 100 | "cell_type": "markdown", 101 | "metadata": {}, 102 | "source": [ 103 | "# 標準 psycopg2 流程:查詢表格欄位(column)" 104 | ] 105 | }, 106 | { 107 | "cell_type": "code", 108 | "execution_count": null, 109 | "metadata": {}, 110 | "outputs": [], 111 | "source": [ 112 | "import os\n", 113 | "import psycopg2\n", 114 | "\n", 115 | "DATABASE_URL = os.popen('heroku config:get DATABASE_URL -a 你-APP-的名字').read()[:-1]\n", 116 | "\n", 117 | "conn = psycopg2.connect(DATABASE_URL, sslmode='require')\n", 118 | "cursor = conn.cursor()\n", 119 | "\n", 120 | "cursor.execute(\"SELECT column_name, data_type FROM information_schema.columns WHERE table_name = 'alpaca_training'\")" 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": null, 126 | "metadata": {}, 127 | "outputs": [], 128 | "source": [ 129 | "data = []\n", 130 | "while True:\n", 131 | " temp = cursor.fetchone()\n", 132 | " if temp:\n", 133 | " data.append(temp)\n", 134 | " else:\n", 135 | " break\n", 136 | "print(data)" 137 | ] 138 | }, 139 | { 140 | "cell_type": "markdown", 141 | "metadata": {}, 142 | "source": [ 143 | "# 標準 psycopg2 流程:刪除表格" 144 | ] 145 | }, 146 | { 147 | "cell_type": "code", 148 | "execution_count": null, 149 | "metadata": {}, 150 | "outputs": [], 151 | "source": [ 152 | "import os\n", 153 | "import psycopg2\n", 154 | "\n", 155 | "DATABASE_URL = os.popen('heroku config:get DATABASE_URL -a 你-APP-的名字').read()[:-1]\n", 156 | "\n", 157 | "conn = psycopg2.connect(DATABASE_URL, sslmode='require')\n", 158 | "cursor = conn.cursor()\n", 159 | "\n", 160 | "delete_table_query = '''DROP TABLE IF EXISTS alpaca_training'''\n", 161 | "\n", 162 | "cursor.execute(delete_table_query)\n", 163 | "conn.commit()" 164 | ] 165 | } 166 | ], 167 | "metadata": { 168 | "kernelspec": { 169 | "display_name": "Python 3", 170 | "language": "python", 171 | "name": "python3" 172 | }, 173 | "language_info": { 174 | "codemirror_mode": { 175 | "name": "ipython", 176 | "version": 3 177 | }, 178 | "file_extension": ".py", 179 | "mimetype": "text/x-python", 180 | "name": "python", 181 | "nbconvert_exporter": "python", 182 | "pygments_lexer": "ipython3", 183 | "version": "3.7.4" 184 | } 185 | }, 186 | "nbformat": 4, 187 | "nbformat_minor": 2 188 | } 189 | -------------------------------------------------------------------------------- /D25/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | {% block title %}{% endblock %} 25 | 26 | 27 | 28 | 29 | 87 | 88 |
89 | {% block main %}{% endblock %} 90 |
91 | 92 | 107 | 108 | 109 | 110 | 111 | 112 | {% block script %}{% endblock %} 113 | 114 | -------------------------------------------------------------------------------- /D27/custom_models/utils.py: -------------------------------------------------------------------------------- 1 | import urllib 2 | import re 3 | import random 4 | import datetime 5 | 6 | # 請 pixabay 幫我們找圖 7 | def get_img_url(img_source, target): 8 | 9 | img_source_dict = { 10 | 'google': [f"https://www.google.com/search?{urllib.parse.urlencode(dict([['tbm', 'isch'], ['q', target]]))}/", 11 | 'img data-src="\S*"', 12 | 14, 13 | -1], 14 | 'pixabay': [f"https://pixabay.com/images/search/{urllib.parse.urlencode(dict([['q', target]]))[2:]}/", 15 | 'img srcset="\S*\s\w*,', 16 | 12, 17 | -4], 18 | 'unsplash': [f"https://unsplash.com/s/photos/{urllib.parse.urlencode(dict([['q', target]]))[2:]}/", 19 | 'srcSet="\S* ', 20 | 8, 21 | -1]} 22 | 23 | url = img_source_dict[img_source][0] 24 | headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36'} 25 | 26 | req = urllib.request.Request(url, headers = headers) 27 | conn = urllib.request.urlopen(req) 28 | 29 | print('fetch page finish') 30 | 31 | img_list = [] 32 | 33 | for match in re.finditer(img_source_dict[img_source][1], str(conn.read())): 34 | img_list.append(match.group()[img_source_dict[img_source][2]:img_source_dict[img_source][3]]) 35 | 36 | random_img_url = random.choice(img_list) 37 | print('fetch img url finish') 38 | print(random_img_url) 39 | 40 | return random_img_url 41 | 42 | # 準備查詢的資料 43 | def prepare_record(text): 44 | text_list = text.split('\n') 45 | 46 | month = text_list[0].split(' ')[0].split('/')[0] 47 | day = text_list[0].split(' ')[0].split('/')[1] 48 | d = datetime.date(datetime.date.today().year, int(month), int(day)) 49 | 50 | record_list = [] 51 | 52 | time_format = '%H:%M' 53 | 54 | for i in text_list[1:]: 55 | temp_list = i.split(' ') 56 | 57 | temp_name = temp_list[0] 58 | temp_training = temp_list[1] 59 | 60 | temp_start = datetime.datetime.strptime(temp_list[2].split('-')[0], time_format) 61 | temp_end = datetime.datetime.strptime(temp_list[2].split('-')[1], time_format) 62 | temp_duration = temp_end - temp_start 63 | 64 | record = (temp_name, temp_training, temp_duration, d) 65 | record_list.append(record) 66 | 67 | return record_list 68 | 69 | # 上 google 找翻譯 70 | def get_translate(text): 71 | 72 | translate = f'{text} 英文' 73 | 74 | url = f"https://www.google.com/search?{urllib.parse.urlencode(dict([['oq', translate], ['q', translate]]))}/" 75 | headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36'} 76 | 77 | req = urllib.request.Request(url, headers = headers) 78 | conn = urllib.request.urlopen(req) 79 | 80 | data = conn.read() 81 | 82 | pattern = '\S*' 83 | match = re.search(pattern, str(data)) 84 | 85 | return match.group()[19:-7] 86 | 87 | # 根據輸入的參數製造 FlexMessage 88 | def prepare_img_search_flex(text, translate, random_img_url): 89 | contents ={"type": "bubble", 90 | "header": { 91 | "type": "box", 92 | "layout": "horizontal", 93 | "contents": [ 94 | {"type": "text", "text": text, "size": "xl", "weight": "bold"}, 95 | {"type": "text", "text": translate, "size": "lg", "color": "#888888", "align": "end", "gravity": "bottom"} 96 | ] 97 | }, 98 | "hero": { 99 | "type": "image", 100 | "url": random_img_url, 101 | "size": "full", 102 | "aspect_ratio": "20:13", 103 | "aspect_mode": "cover" 104 | }, 105 | "body": { 106 | "type": "box", 107 | "layout": "vertical", 108 | "spacing": "md", 109 | "contents": [ 110 | {"type": "text", "text": "圖片的出處在這裡", "size": "md", "weight": "bold"} 111 | ] 112 | }, 113 | "footer": { 114 | "type": "box", 115 | "layout": "vertical", 116 | "contents": [ 117 | {"type": "spacer", "size": "md"}, 118 | {"type": "button", "style": "primary", "color": "#1DB446", 119 | "action": {"type": "uri", "label": "GO", "uri": random_img_url}} 120 | ] 121 | } 122 | } 123 | return contents 124 | 125 | # Day24: 列出每個欄位中的所有選項 126 | def get_unique(table): 127 | unique_alpaca_name = {i[1] for i in table} 128 | unique_training = {i[2] for i in table} 129 | unique_date = {i[4] for i in table} 130 | 131 | return sorted(unique_alpaca_name), sorted(unique_training), sorted(unique_date) 132 | 133 | # Day27: 準備好可以轉換成適合 JavaScript 的資料 134 | def total_seconds(table): 135 | new_table = [] 136 | for i in table: 137 | new_table.append((i[0], i[1], i[2], int(i[3].total_seconds()), str(i[4]))) 138 | return new_table --------------------------------------------------------------------------------