-
Notifications
You must be signed in to change notification settings - Fork 0
Hang out feature/chatbot #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
5d7b4de
b4e9cba
f173b43
1c6da13
c0ffbdd
cb4d756
af9b479
2245f6a
9b16ff3
e493b63
061ff71
1ccf21e
1cbb57f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| *.DS_Store |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| web: gunicorn app_core:app –preload |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,216 @@ | ||
| from __future__ import unicode_literals | ||
| from flask import Flask, request, abort | ||
| import configparser | ||
| import datetime as dt | ||
|
|
||
| from linebot import LineBotApi, WebhookHandler | ||
| from linebot.exceptions import InvalidSignatureError | ||
| from linebot.models import MessageEvent, TextMessage, ImageSendMessage, PostbackEvent, LocationMessage, ImageMessage | ||
|
|
||
| from controllers import basic_info, text_insert, new_activity, new_registration, record, edit, verify, climate, new_page | ||
|
|
||
| app = Flask(__name__) | ||
|
|
||
| # LINE 聊天機器人的基本資料 | ||
| config = configparser.ConfigParser() | ||
| config.read("config.ini") | ||
|
|
||
| line_bot_api = LineBotApi(config.get("line-bot", "channel_access_token")) | ||
| handler = WebhookHandler(config.get("line-bot", "channel_secret")) | ||
| mapbox_key = config.get("mapbox", "access_token") | ||
| climate_key = config.get("climate", "authorization") | ||
|
|
||
| today_tw = (dt.datetime.now() + dt.timedelta(hours = 8)).date() | ||
|
|
||
| # 接收 LINE 的資訊 | ||
| @app.route("/callback", methods=["POST"]) | ||
| def callback(): | ||
| signature = request.headers["X-Line-Signature"] | ||
|
|
||
| body = request.get_data(as_text=True) | ||
| app.logger.info("Request body: " + body) | ||
| print(f"event_body:{body}") | ||
|
|
||
| global line_id, user_condition | ||
| line_id = basic_info.line_id(request.get_data()) | ||
| user_condition = [f"line_id = '{line_id}'"] | ||
|
|
||
| global user_id, init_condition | ||
| user_id = basic_info.user_id(user_condition) | ||
| init_condition = ["condition = 'initial'"] | ||
| init_condition.append(f"user_id = {user_id}" if user_id else f"user_id = Null") | ||
|
|
||
| global activity_info, user_info, registration_info | ||
| activity_info, registration_info = basic_info.basic_info(init_condition) | ||
| user_info = basic_info.user_info(user_condition) | ||
|
|
||
| try: | ||
| handler.handle(body, signature) | ||
| except InvalidSignatureError: | ||
| abort(400) | ||
|
|
||
| return 'OK' | ||
|
|
||
|
|
||
| @handler.add(MessageEvent, message = TextMessage) | ||
| def message_event(event): | ||
| user_text = event.message.text | ||
|
|
||
| msg = text_insert.text_insert(user_text, user_id, activity_info, user_info, registration_info, init_condition, user_condition) | ||
|
|
||
| line_bot_api.reply_message( | ||
| event.reply_token, | ||
| msg | ||
| ) | ||
|
|
||
| @handler.add(PostbackEvent) | ||
| def postback_event(event): | ||
| postback_data = event.postback.data | ||
|
|
||
| if postback_data == "我要開團": | ||
| msg = new_activity.choose_activity_type(user_id) | ||
|
|
||
| elif "開團活動類型" in postback_data: | ||
| activity_type = postback_data.split("_")[1] | ||
|
|
||
| msg = new_activity.new_activity(line_id, user_id, activity_type, user_info, user_condition, init_condition) | ||
|
|
||
| elif "回覆活動時間" in postback_data: | ||
| activity_date, activity_time = event.postback.params["datetime"].split("T") | ||
|
|
||
| msg = new_activity.after_replay_activity_time(activity_date, activity_time, init_condition, user_info) | ||
|
|
||
| elif "回覆報名截止時間" in postback_data: | ||
| due_date = event.postback.params["date"] | ||
|
|
||
| msg = new_activity.after_replay_due_date(due_date, init_condition, user_info) | ||
|
|
||
| elif "修改" in postback_data: | ||
| table, column = postback_data.split("_", 2)[1:] | ||
|
|
||
| msg = edit.edit(table, column, init_condition, user_condition, activity_info) | ||
|
|
||
| elif "確認開團" in postback_data: | ||
| msg = new_activity.confirm_new_activity(init_condition) | ||
|
|
||
| elif "取消開團" in postback_data: | ||
| msg = new_activity.cancel_new_activity(init_condition) | ||
| #------------------------------------------------------------------------------------- | ||
| elif postback_data == "我要報名": | ||
| msg = new_registration.choose_activity_type(user_id) | ||
|
|
||
| elif "報名活動類型" in postback_data: | ||
| activity_type = postback_data.split("_")[1] | ||
|
|
||
| msg = new_registration.activity_for_registration(today_tw, activity_type) | ||
|
|
||
| elif "詳細資訊" in postback_data : | ||
| activity_id = postback_data.split("_")[1] | ||
|
|
||
| msg = new_registration.activity_detail(activity_id) | ||
|
|
||
| elif "立即報名" in postback_data: | ||
| activity_id = postback_data.split("_")[1] | ||
|
|
||
| msg = new_registration.new_registration(line_id, user_id, activity_id, user_info, user_condition, init_condition) | ||
|
|
||
| elif "確認報名" in postback_data: | ||
| activity_id = postback_data.split("_")[1] | ||
|
|
||
| msg = new_registration.confirm_registration(activity_id, init_condition) | ||
|
|
||
| elif "取消報名" in postback_data: | ||
| registration_id, activity_id = postback_data.split('_')[1:] | ||
|
|
||
| msg = new_registration.cancel_registration(registration_id, activity_id) | ||
| #------------------------------------------------------------------------------------- | ||
| elif postback_data == "我的開團": | ||
| status = "開團" | ||
|
|
||
| msg = record.choose_record_type(user_id, status) | ||
|
|
||
| elif "開團紀錄" in postback_data: | ||
| record_type = postback_data.split("_")[1] | ||
|
|
||
| msg = record.activity_record(user_id, record_type, today_tw) | ||
|
|
||
| elif "開團資訊" in postback_data: | ||
| activity_id = postback_data.split("_")[1] | ||
|
|
||
| msg = record.activity_information(activity_id) | ||
|
|
||
| #主揪查看報名者資訊(報名者暱稱、電話) | ||
| elif "報名者資訊" in postback_data: | ||
| activity_id = postback_data.split("_")[1] | ||
|
|
||
| msg = record.attendee_information(activity_id) | ||
|
|
||
| #主揪提早關團 | ||
| elif "結束報名" in postback_data: | ||
| activity_id = postback_data.split("_")[1] | ||
|
|
||
| msg = record.close_activity(activity_id) | ||
| #------------------------------------------------------------------------------------- | ||
| elif postback_data == "我的報名": | ||
| status = "報名" | ||
|
|
||
| msg = record.choose_record_type(user_id, status) | ||
|
|
||
| elif "報名紀錄" in postback_data: | ||
| record_type = postback_data.split("_")[1] | ||
|
|
||
| msg = record.registration_record(user_id, record_type, today_tw) | ||
|
|
||
| elif "查報名" in postback_data: | ||
| activity_id, registration_id = postback_data.split('_')[1:] | ||
|
|
||
| msg = record.activity_registration_information(activity_id, registration_id, user_condition) | ||
| #------------------------------------------------------------------------------------- | ||
| elif "newPage" in postback_data: | ||
| button_type, i, record_type = postback_data.split("_")[0:3] | ||
| i = int(i) # i代表要從資料庫中第幾個資料開始呈現,下一頁的 i=i+8,上一頁的 i=i-8 | ||
|
|
||
| msg = new_page.new_page(button_type, i, record_type, today_tw, user_id) | ||
| #------------------------------------------------------------------------------------- | ||
| elif "climate" in postback_data: | ||
| activity_id = postback_data.split("_")[1] | ||
|
|
||
| location_longitude, location_latitude, activity_date, activity_time = climate.location_datetime(activity_id) | ||
|
|
||
| county, district = climate.geo_data(mapbox_key, location_longitude, location_latitude) | ||
|
|
||
| msg = climate.climate(climate_key, county, district, activity_date, activity_time) | ||
|
|
||
| line_bot_api.reply_message( | ||
| event.reply_token, | ||
| msg | ||
| ) | ||
|
|
||
| @handler.add(MessageEvent, message = LocationMessage) | ||
| def location(event): | ||
| # 取得地點名稱、經度、緯度, 如果該地點沒有名稱,則用地址取代 | ||
| location_info = event.message | ||
| position, latitude, longitude, address = location_info.title, location_info.latitude, location_info.longitude, location_info.address | ||
|
|
||
| msg = new_activity.after_replay_location(position, latitude, longitude, address, init_condition, user_info) | ||
|
|
||
| line_bot_api.reply_message( | ||
| event.reply_token, | ||
| msg | ||
| ) | ||
|
|
||
| @handler.add(MessageEvent, message = ImageMessage) | ||
| def pic(event): | ||
| photo_content = line_bot_api.get_message_content(event.message.id) | ||
| imgur_config = [config.get('imgur', 'client_id'), config.get('imgur', 'client_secret'), config.get('imgur', 'access_token'), config.get('imgur', 'refresh_token'), config.get('imgur', 'album_id')] | ||
|
|
||
| msg = new_activity.after_replay_photo(photo_content, imgur_config, user_info, activity_info, init_condition, user_id) | ||
|
|
||
| line_bot_api.reply_message( | ||
| event.reply_token, | ||
| msg | ||
| ) | ||
|
|
||
| if __name__ == "__main__": | ||
| app.run() | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| from models import CallDatabase | ||
| from templates import flexmsg_activity | ||
| import json | ||
|
|
||
| activity_columns = flexmsg_activity.columns | ||
| user_columns = ["name", "phone"] | ||
|
|
||
| def line_id(request_data): | ||
| try: | ||
| line_id = json.loads(request_data)["events"][0]["source"]["userId"] | ||
| except: | ||
| line_id = None | ||
| return line_id | ||
|
|
||
| def user_id(user_condition): | ||
| user_id = CallDatabase.get_data("users", ["id"], condition = user_condition, all_data = False) | ||
| user_id = user_id[0] if user_id else None | ||
| return user_id | ||
|
|
||
| def basic_info(init_condition): | ||
| activity_info = CallDatabase.get_data("activity", activity_columns[:-2], condition = init_condition, all_data = False) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove the space |
||
| registration_info = CallDatabase.get_data("registration", ["activity_id", "id"], condition = init_condition, all_data = False) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove the space |
||
| return activity_info, registration_info | ||
|
|
||
| def user_info(user_condition): | ||
| user_info = CallDatabase.get_data("users", user_columns, condition = user_condition, all_data = False) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove the space |
||
| return user_info | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| import datetime as dt | ||
| import requests | ||
| from models import CallDatabase | ||
| from templates import flexmsg_climate | ||
|
|
||
| def location_datetime(activity_id): | ||
| condition = [f"id = {activity_id}"] | ||
| location_datetime = CallDatabase.get_data("activity", ["location_longitude", "location_latitude", "activity_date", "activity_time"], condition = condition, all_data = False) | ||
| return location_datetime | ||
|
|
||
| def geo_data(mapbox_key, location_longitude, location_latitude): | ||
| geo_url = f"https://api.mapbox.com/geocoding/v5/mapbox.places/{location_longitude},{location_latitude}.json" | ||
| my_params = {"language": "zh-tw", "access_token": mapbox_key} | ||
| re_mapbox = requests.get(geo_url, params = my_params) | ||
| county = re_mapbox.json()["features"][0]["context"][-2]["text"] | ||
| district = re_mapbox.json()["features"][0]["context"][-3]["text"] | ||
| print(county, district) | ||
| return county, district | ||
|
|
||
| def climate(climate_key, county, district, activity_date, activity_time): | ||
| try: | ||
| # climate_data | ||
| county_code = {'宜蘭縣': 'F-D0047-003', '桃園市': 'F-D0047-007', '新竹縣': 'F-D0047-011', '苗栗縣': 'F-D0047-015', '彰化縣': 'F-D0047-019', '南投縣': 'F-D0047-023', '雲林縣': 'F-D0047-027', '嘉義縣': 'F-D0047-031', '屏東縣': 'F-D0047-035', '臺東縣': 'F-D0047-039', '花蓮縣': 'F-D0047-043', '澎湖縣': 'F-D0047-047', '基隆市': 'F-D0047-051', '新竹市': 'F-D0047-055', '嘉義市': 'F-D0047-059', '臺北市': 'F-D0047-063', '高雄市': 'F-D0047-067', '新北市': 'F-D0047-071', '臺中市': 'F-D0047-075', '臺南市': 'F-D0047-079', '連江縣': 'F-D0047-083', '金門縣': 'F-D0047-087'} | ||
| climate_url = f"https://opendata.cwb.gov.tw/api/v1/rest/datastore/{county_code.get(county)}" | ||
| my_params = {"Authorization": climate_key, "locationName": district} | ||
|
|
||
| re_climate = requests.get(climate_url, params = my_params).json() | ||
| weather_element = re_climate["records"]["locations"][0]["location"][0]["weatherElement"] | ||
| UVI = weather_element.pop(9) | ||
|
|
||
| start_time = dt.datetime.strptime(weather_element[0]["time"][0]["startTime"], "%Y-%m-%d %H:%M:%S") | ||
| dt_list = [start_time] + [dt.datetime.strptime(time["endTime"], "%Y-%m-%d %H:%M:%S") for time in weather_element[0]["time"]] | ||
| activity_dt = dt.datetime.strptime(str(activity_date) + str(activity_time), "%Y-%m-%d%H:%M:%S") | ||
| i = 0 | ||
| while i < len(dt_list) - 1: | ||
| if dt_list[i] <= activity_dt < dt_list[i+1]: | ||
| break | ||
| else: | ||
| i += 1 | ||
| print(i) | ||
|
|
||
| if i == len(dt_list) - 1: | ||
| print("僅提供一週內的天氣預報!") | ||
| return flexmsg_climate.no_climate() | ||
|
|
||
| else: | ||
| climate_data = {item["description"]: list(item["time"][i]["elementValue"][0].values()) for item in weather_element} | ||
| UVI = {item["startTime"].split()[0]:[[row["value"], row["measures"]] for row in item["elementValue"]] for item in UVI["time"]} | ||
| uvi = [row[0] for row in UVI.get(str(activity_date), [])] | ||
| print(activity_dt, weather_element[0]["time"][i], climate_data, end = "\n") | ||
|
|
||
| climate_lst = ["12小時降雨機率", "天氣現象", "平均溫度", "最高溫度", "最低溫度", "平均相對濕度", "風向", "最大風速"] | ||
| climate_info = [climate_data[item][0] for item in climate_lst] + [uvi] | ||
| return flexmsg_climate.climate(activity_date, county, district, climate_info) | ||
| except: | ||
| return flexmsg_climate.no_data() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| from models import CallDatabase | ||
| from templates import flexmsg_question | ||
|
|
||
| def edit(table, column, init_condition, user_condition, activity_info): | ||
| activity_date = activity_info[2] if activity_info else None | ||
| condition = init_condition if table == "activity" else user_condition | ||
| # 將資料庫中該題的內容清除 | ||
| CallDatabase.update(table, columns = [column], values = ["Null"], condition = condition) | ||
| msg = flexmsg_question.question(column, progress = 0, activity_date = activity_date) | ||
| return msg | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the space => ..., condition=user_condition, all_data=False